Skip to content

feat(api): add Server-Sent Events (SSE) infrastructure#11556

Open
puchy22 wants to merge 13 commits into
masterfrom
PROWLER-1414-add-sse-capabilities-to-the-backend
Open

feat(api): add Server-Sent Events (SSE) infrastructure#11556
puchy22 wants to merge 13 commits into
masterfrom
PROWLER-1414-add-sse-capabilities-to-the-backend

Conversation

@puchy22

@puchy22 puchy22 commented Jun 11, 2026

Copy link
Copy Markdown
Member

Context

The API needs a reusable foundation for Server-Sent Events (SSE) so endpoints can push a one-way stream of events to clients over a single long-lived HTTP connection (live progress, token-by-token LLM output, cross-client sync). This work lands the shared SSE infrastructure and developer documentation so feature endpoints can adopt it. No existing endpoint is converted to SSE in this PR — the goal is to provide the basis.

Description

Adds the platform SSE layer that wires django-eventstream into the API, plus the runtime and auth changes needed to serve streams:

  • Dependencies: add django-eventstream and bump gunicorn to the release that ships the native asgi worker.
  • ASGI runtime: run gunicorn with the native asgi worker against config.asgi so SSE streams are parked on the event loop instead of holding a sync worker per connection (sync CRUD views keep running in the thread-sensitive executor). preload_app is disabled under DEBUG so dev reload works; dev and prod entrypoints point at the ASGI app.
  • SSEAuthentication: extends the standard JWT/API-key stack with an ?access_token=<jwt> query-parameter fallback (RFC 6750 §2.3), since browser EventSource cannot set the Authorization header. The query path accepts a JWT only; API keys remain header-only.
  • SSE infrastructure (api/sse/): BaseSSEViewSet (subclass + implement get_channels, reuses the regular DRF auth/RBAC/tenant-transaction stack), SSEChannelManager (tenant gate via the tenant id embedded in the channel name), and make_channel_name/tenant_id_from_channel (single source of truth for the <prefix>:<tenant_id>:<resource_id> channel format). Valkey Pub/Sub backend configured on a dedicated DB.
  • Docs: a Developer Guide page covering when to use SSE, the architecture/ASGI transport, a step-by-step example for adding an endpoint, the resource.verb event-naming convention, auth, the tenant-isolation model, and reconnect/state recovery.

New dependencies: django-eventstream==5.3.3, gunicorn==26.0.0 (was 23.0.0).

Steps to review

  1. Read the new developer guide (docs/developer-guide/server-sent-events.mdx) for the intended architecture and the worked example.
  2. Review the SSE package (api/src/backend/api/sse/): the base viewset, the channel manager's two-layer authorization (resource lookup in get_channels + tenant gate in can_read_channel), and the channel-name helpers.
  3. Review SSEAuthentication in api/src/backend/api/authentication.py and the header-wins / query-fallback precedence.
  4. Review the ASGI runtime changes: config/guniconf.py, docker-entrypoint.sh, and the settings wiring in config/django/base.py + config/settings/eventstream.py.

Checklist

Community Checklist
  • This feature/issue is listed in here or roadmap.prowler.com
  • Is it assigned to me, if not, request it via the issue/feature in here or Prowler Community Slack

SDK/CLI

  • Are there new checks included in this PR? No

API

  • All issue/task requirements work as expected on the API
  • Endpoint response output (if applicable) — N/A, no endpoint added; infrastructure only
  • EXPLAIN ANALYZE output for new/modified queries or indexes (if applicable) — N/A
  • Performance test results (if applicable) — N/A
  • Any other relevant evidence of the implementation (if applicable)
  • Verify if API specs need to be regenerated. — No new endpoints; specs unchanged
  • Check if version updates are required (e.g., specs, uv, etc.). — uv.lock updated
  • Ensure new entries are added to CHANGELOG.md, if applicable. — added under [1.32.0] (Prowler UNRELEASED)

License

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

Summary by CodeRabbit

  • New Features
    • Added Server-Sent Events (SSE) support with tenant-scoped channel delivery and improved streaming endpoint authentication for browser clients via query-token fallback.
  • Chores
    • Updated the server runtime to use ASGI-compatible Gunicorn for both development and production.
    • Upgraded Gunicorn to 26.0.0 and added django-eventstream for SSE delivery.
  • Documentation
    • Added a developer guide for implementing and testing SSE endpoints.
    • Added an unreleased changelog entry for the upcoming version.

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds Server-Sent Events (SSE) platform: ASGI/Gunicorn runtime changes, django-eventstream dependency and settings, SSE authentication fallback, channel-name utilities, BaseSSEViewSet and tenant-aware SSEChannelManager, tests, developer guide, and changelog entry.

Changes

Server-Sent Events Platform

Layer / File(s) Summary
Runtime dependencies and ASGI server configuration
api/pyproject.toml, api/docker-entrypoint.sh, api/src/backend/config/guniconf.py, api/src/backend/config/django/base.py, api/src/backend/config/settings/eventstream.py
Django-eventstream dependency added and gunicorn upgraded to v26 for ASGI. Container entrypoint now starts Gunicorn ASGI in both dev and prod modes. Gunicorn configured with ASGI worker class and Django ASGI_APPLICATION setting points to config.asgi:application for long-lived streaming support. Eventstream settings module configures Valkey Pub/Sub bus, channel manager, and allowed EventSource headers.
Channel naming and tenant extraction utilities
api/src/backend/api/sse/utils.py, api/src/backend/api/sse/__init__.py, api/src/backend/api/tests/test_sse.py
Colon-separated channel naming format (prefix:tenant_id:resource_id) with helpers make_channel_name() and tenant_id_from_channel(). Tests verify round-tripping, string tenant handling, hyphenated prefixes, and graceful handling of malformed channels. Package exports public API.
SSE-aware authentication for header-less clients
api/src/backend/api/authentication.py, api/src/backend/api/tests/test_authentication.py
SSEAuthentication extends existing JWT/API-key auth to support EventSource clients that cannot set headers, falling back to access_token query parameter validation. Tests confirm standard header auth remains unchanged and query-only fallback validates via JWT.
SSE ViewSet base and tenant-scoped channel manager
api/src/backend/api/sse/base_views.py, api/src/backend/api/sse/channelmanager.py, api/src/backend/api/tests/test_sse.py
BaseSSEViewSet overrides list() to populate channels from get_channels() and streams via django_eventstream. SSEChannelManager enforces tenant isolation: authenticates users, extracts tenant from channel name, and only allows reads for tenant members. Tests validate authorization, malformed channel rejection, and channel reliability status.
Documentation and changelog
api/CHANGELOG.md, docs/developer-guide/server-sent-events.mdx, docs/docs.json
Changelog entry for SSE infrastructure. Developer guide explains use cases, platform components, endpoint implementation workflow, event conventions, authentication patterns, tenant isolation mechanics, reconnect/recovery model, local testing with curl, and test coverage expectations. Navigation updated.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.51% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'feat(api): add Server-Sent Events (SSE) infrastructure' directly and concisely summarizes the main change: introducing SSE infrastructure to the API. It is specific, clear, and aligns with the primary objective of the PR.
Description check ✅ Passed The PR description comprehensively covers context, implementation details, dependencies, architecture, and review steps. It includes all required checklist items marked as completed and provides thorough explanations of the changes across all affected areas (dependencies, runtime, authentication, infrastructure, and documentation).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch PROWLER-1414-add-sse-capabilities-to-the-backend

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

✅ All necessary CHANGELOG.md files have been updated.

@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Conflict Markers Resolved

All conflict markers have been successfully resolved in this pull request.

@mintlify

mintlify Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
prowler 🟢 Ready View Preview Jun 11, 2026, 2:32 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

🔒 Container Security Scan

Image: prowler-api:9c59d8f
Last scan: 2026-06-15 09:39:36 UTC

📊 Vulnerability Summary

Severity Count
🔴 Critical 22
Total 22

16 package(s) affected

⚠️ Action Required

Critical severity vulnerabilities detected. These should be addressed before merging:

  • Review the detailed scan results
  • Update affected packages to patched versions
  • Consider using a different base image if updates are unavailable

📋 Resources:

@codecov

codecov Bot commented Jun 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.42857% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.04%. Comparing base (a394c0f) to head (e9101c6).
⚠️ Report is 12 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #11556      +/-   ##
==========================================
+ Coverage   94.02%   94.04%   +0.01%     
==========================================
  Files         241      247       +6     
  Lines       35705    35927     +222     
==========================================
+ Hits        33573    33787     +214     
- Misses       2132     2140       +8     
Flag Coverage Δ
api 94.04% <96.42%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
prowler ∅ <ø> (∅)
api 94.04% <96.42%> (+0.01%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@puchy22 puchy22 marked this pull request as ready for review June 11, 2026 15:53
@puchy22 puchy22 requested review from a team as code owners June 11, 2026 15:53

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@api/src/backend/api/sse/utils.py`:
- Around line 34-36: Change the channel-shape check to require exactly three
segments and fail otherwise: replace the current len(segments) < 3 check with
len(segments) != 3 so channels with more than three segments are rejected;
additionally, after splitting, validate the first segment equals the expected
prefix constant (e.g., CHANNEL_PREFIX or the literal used elsewhere) and
validate the tenant_id segment is a UUID (use uuid.UUID(...) in a try/except) to
prevent separator-injection and non-canonical names when extracting tenant_id
from segments.

In `@api/src/backend/api/tests/test_authentication.py`:
- Around line 413-427: Add a test case in test_authentication.py that verifies
SSEAuthentication.authenticate raises an authentication error when an
access_token query param is present but invalid: create a MagicMock request with
request.query_params = {"access_token": "bad-token"}, patch
"api.authentication.JWTAuthentication" to return a jwt_instance whose
get_validated_token raises rest_framework.exceptions.AuthenticationFailed, call
SSEAuthentication().authenticate(request) and assert that AuthenticationFailed
is raised, and also assert get_validated_token was called with "bad-token" to
ensure the query-token path is exercised.

In `@docs/developer-guide/server-sent-events.mdx`:
- Around line 60-66: Standardize the prose to use "tenant ID" (uppercase)
everywhere while keeping code identifiers like `<tenant_id>` and CHANNEL_PREFIX
(and the example channel format `<prefix>:<tenant_id>:<resource_id>`) unchanged;
update occurrences such as "The tenant id is baked into every channel name" and
"parsing the tenant id embedded in the channel name" to "The tenant ID..." so
all narrative references use the uppercase form, but do not modify code snippets
or symbol names like make_channel_name, CHANNEL_PREFIX, or `<tenant_id>`.
- Line 60: Reword the phrase "owned by your feature" to remove the second-person
possessive in the sentence describing channel format; for example change it to
"provided by the feature", "controlled by the feature", or "assigned by the
feature" so the sentence reads like "The prefix is provided by the feature and
may contain hyphens but never colons (the parser splits on `:`)"; update the doc
text that describes channels and the `make_channel_name` usage accordingly.
- Around line 1-3: Add a Version Badge immediately after the section header
"Server-Sent Events (SSE)" to indicate this is new in Prowler API v1.32.0;
update docs/developer-guide/server-sent-events.mdx by inserting the standard
Version Badge element (matching project badge style) right below the top-level
title line so the page clearly shows "Prowler API v1.32.0" for the new SSE
infrastructure feature.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3e28d44a-07ed-482b-80d9-7f77bd3f1cc7

📥 Commits

Reviewing files that changed from the base of the PR and between 610febb and 64d6255.

⛔ Files ignored due to path filters (1)
  • api/uv.lock is excluded by !**/*.lock, !**/uv.lock
📒 Files selected for processing (15)
  • api/CHANGELOG.md
  • api/docker-entrypoint.sh
  • api/pyproject.toml
  • api/src/backend/api/authentication.py
  • api/src/backend/api/sse/__init__.py
  • api/src/backend/api/sse/base_views.py
  • api/src/backend/api/sse/channelmanager.py
  • api/src/backend/api/sse/utils.py
  • api/src/backend/api/tests/test_authentication.py
  • api/src/backend/api/tests/test_sse.py
  • api/src/backend/config/django/base.py
  • api/src/backend/config/guniconf.py
  • api/src/backend/config/settings/eventstream.py
  • docs/developer-guide/server-sent-events.mdx
  • docs/docs.json

Comment thread api/src/backend/api/sse/utils.py
Comment thread api/src/backend/api/tests/test_authentication.py
Comment thread docs/developer-guide/server-sent-events.mdx
Comment thread docs/developer-guide/server-sent-events.mdx
Comment thread docs/developer-guide/server-sent-events.mdx
Comment thread api/src/backend/api/sse/base_views.py
Comment thread api/src/backend/api/tests/test_authentication.py
puchy22 added 10 commits June 12, 2026 12:32
Add the django-eventstream dependency that backs Server-Sent Events and
bump gunicorn to a release that ships the native asgi worker class, so
SSE streams can run on the event loop.
Run gunicorn with the native asgi worker against config.asgi so SSE
streams are parked on the event loop instead of holding a sync worker
per open connection; sync CRUD views keep running in the thread-sensitive
executor. Disable preload under DEBUG so dev reload picks up edited code,
and point the dev and prod entrypoints at the ASGI application.
Browser EventSource cannot set the Authorization header, so add an
SSEAuthentication class that extends the standard JWT/API-key stack with
an ?access_token=<jwt> query-parameter fallback (RFC 6750 section 2.3),
consulted only when no Authorization header is present. The query path
accepts a JWT only; API keys remain header-only.
Add the platform SSE layer that wires django-eventstream into the API:

- BaseSSEViewSet: a base viewset features subclass to expose an SSE
  endpoint, reusing the regular DRF stack (auth, RBAC permissions, tenant
  transaction) and delegating the stream to django-eventstream.
- SSEChannelManager: resolves the channel set off the request and enforces
  a tenant gate by parsing the tenant id embedded in the channel name.
- make_channel_name/tenant_id_from_channel: the single source of truth for
  the <prefix>:<tenant_id>:<resource_id> channel format.
- eventstream settings: Valkey Pub/Sub backend on a dedicated DB, the
  channel manager, and allowed headers; registered in Django settings.

No endpoint streams over SSE yet; this is the reusable base.
Document the SSE infrastructure for backend developers: when to use SSE,
the architecture and ASGI transport, a step-by-step worked example for
adding an endpoint to a feature, the resource.verb event-naming
convention, authentication, the tenant-isolation model, and reconnect/
state-recovery. Register the page in the Developer Guide navigation.
Enforce the canonical <prefix>:<tenant_id>:<resource_id> contract: make_channel_name now raises ValueError when any segment contains the ':' separator, and tenant_id_from_channel requires exactly three segments so a crafted name cannot slip a valid tenant UUID into position 1 while carrying extra segments.
Add an error-path test asserting SSEAuthentication.authenticate raises AuthenticationFailed when the access_token query param is present but invalid, complementing the existing valid-token fallback test.
Drive a real DRF request through the full viewset stack (auth, RLS, content negotiation, channel manager) and assert events() returns an SSE StreamingHttpResponse, guarding the DRF-request-into-django-eventstream path from silent regressions.
Mark the Server-Sent Events guide as new in Prowler API v1.32.0 with the standard VersionBadge component.
@puchy22 puchy22 force-pushed the PROWLER-1414-add-sse-capabilities-to-the-backend branch from 64d6255 to 7820d68 Compare June 12, 2026 10:38

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@api/src/backend/api/authentication.py`:
- Around line 120-123: The code creates a new JWTAuthentication instance instead
of using the shared backend, causing divergence from
CombinedJWTOrAPIKeyAuthentication.jwt_auth; replace the direct instantiation
(JWTAuthentication()) with a reference to the shared backend
(CombinedJWTOrAPIKeyAuthentication.jwt_auth) when validating the raw_token and
retrieving the user so SSE fallback uses the same configured JWT backend; update
calls to get_validated_token and get_user to use that shared jwt_auth instance.

In `@api/src/backend/api/sse/channelmanager.py`:
- Around line 13-29: get_channels_for_request currently returns
request.sse_channels without checking the active JWT tenant; change it to
filter/validate every channel in request.sse_channels against request.tenant_id
(the tenant set by BaseRLSViewSet / request.auth["tenant_id"]) by parsing
tenant_id_from_channel(channel) and only returning channels whose embedded
tenant equals request.tenant_id (fail-closed by excluding mismatches or
malformed channels); keep can_read_channel as the secondary membership backstop
but ensure the primary authorization uses request.tenant_id before handing
channels to django-eventstream.

In `@api/src/backend/api/tests/test_authentication.py`:
- Around line 392-400: Add a new assertion to the existing
test_header_present_delegates_to_super that sets both an Authorization header
and a query access_token (e.g., request.query_params = {"access_token":
"query-token"}) and verifies SSEAuthentication().authenticate(request) delegates
to the superclass authenticate (patch
SSEAuthentication.__bases__[0].authenticate as in the test) and returns the
super result; this ensures the header takes precedence over the ?access_token=
fallback.

In `@api/src/backend/config/guniconf.py`:
- Around line 28-35: The preload/reload and logging decisions are using the
config variable DEBUG instead of the actual runtime Django settings; update the
module to read settings.DEBUG and settings.LOGGING from the active settings
module (via django.conf.settings or by loading DJANGO_SETTINGS_MODULE) and use
those values when computing preload_app, reload and any logging configuration;
specifically change references that set preload_app = not DEBUG and any LOGGING
usage to use settings.DEBUG and settings.LOGGING (ensure settings is
imported/initialized before use) so Gunicorn behavior matches the active
settings module.

In `@docs/developer-guide/server-sent-events.mdx`:
- Around line 27-35: Update the documentation to use consistent
repository-relative paths (prefer the existing repo convention like
api/src/backend/api/...) for all referenced files: replace instances of
api/sse/base_views.py, api/sse/channelmanager.py, api/authentication.py,
api/sse/utils.py, config/settings/eventstream.py and api/tests/test_sse.py with
their repository-relative equivalents (e.g.,
api/src/backend/api/sse/base_views.py,
api/src/backend/api/sse/channelmanager.py,
api/src/backend/api/authentication.py, api/src/backend/api/sse/utils.py,
api/src/backend/config/settings/eventstream.py,
api/src/backend/api/tests/test_sse.py) and ensure all other mentions on the page
use the same convention so paths are uniform throughout the guide.
- Line 15: The documentation uses sentence case for several section headers;
update each listed header to Title Case to match the docs standard — e.g.,
change "When to use SSE", "How it works", "Local development" and the other
occurrences (lines referenced: the headers with texts at 25, 37, 41, 56, 164,
181, 200, 209, 218, 235) to Title Case (e.g., "When to Use SSE", "How It Works",
"Local Development"); ensure every header in
docs/developer-guide/server-sent-events.mdx follows Title-Case capitalization
consistently.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3e3c1b2a-d789-4931-ac69-b50dae15e9b6

📥 Commits

Reviewing files that changed from the base of the PR and between 64d6255 and 7820d68.

⛔ Files ignored due to path filters (1)
  • api/uv.lock is excluded by !**/*.lock, !**/uv.lock
📒 Files selected for processing (15)
  • api/CHANGELOG.md
  • api/docker-entrypoint.sh
  • api/pyproject.toml
  • api/src/backend/api/authentication.py
  • api/src/backend/api/sse/__init__.py
  • api/src/backend/api/sse/base_views.py
  • api/src/backend/api/sse/channelmanager.py
  • api/src/backend/api/sse/utils.py
  • api/src/backend/api/tests/test_authentication.py
  • api/src/backend/api/tests/test_sse.py
  • api/src/backend/config/django/base.py
  • api/src/backend/config/guniconf.py
  • api/src/backend/config/settings/eventstream.py
  • docs/developer-guide/server-sent-events.mdx
  • docs/docs.json

Comment thread api/src/backend/api/authentication.py Outdated
Comment thread api/src/backend/api/sse/channelmanager.py Outdated
Comment thread api/src/backend/api/tests/test_authentication.py
Comment thread api/src/backend/config/guniconf.py
Comment thread docs/developer-guide/server-sent-events.mdx
Comment thread docs/developer-guide/server-sent-events.mdx
josema-xyz
josema-xyz previously approved these changes Jun 12, 2026
The ?access_token= fallback in SSEAuthentication created a fresh JWTAuthentication() instead of the shared CombinedJWTOrAPIKeyAuthentication.jwt_auth instance used by the header path. Reuse self.jwt_auth so the query-token fallback stays on the same configured backend if the parent auth stack is ever customized.

Patch the shared instance instead of the constructor in the SSE auth tests.
SSEChannelManager.get_channels_for_request returned every channel stashed on the request, leaving can_read_channel (a membership check) as the only tenant gate. A user belonging to multiple tenants could then read another tenant's stream if a viewset ever returned the wrong channel set.

Filter request.sse_channels against the active JWT tenant (request.tenant_id, set by BaseRLSViewSet) before handing channels to django-eventstream, keeping can_read_channel as the membership backstop. Fail-closed: a missing or unparseable request tenant, or a malformed channel, yields no channels.

Add type hints and docstrings to the manager methods and cover the cross-tenant, malformed, and missing-tenant cases in the tests.
josema-xyz
josema-xyz previously approved these changes Jun 12, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 15, 2026
pedrooot
pedrooot previously approved these changes Jun 15, 2026
@puchy22 puchy22 dismissed stale reviews from pedrooot, coderabbitai[bot], and josema-xyz via e9101c6 June 15, 2026 09:17

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
api/src/backend/api/sse/channelmanager.py (1)

64-64: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Wrap the membership lookup in an RLS transaction.

user.is_member_of_tenant(tenant_id) queries memberships, but this channel-manager path is outside a ViewSet and currently runs without rls_transaction(tenant_id). That bypasses the project’s RLS enforcement contract for tenant-scoped reads.

Proposed fix
-        return user.is_member_of_tenant(tenant_id)
+        with rls_transaction(tenant_id):
+            return user.is_member_of_tenant(tenant_id)

Also import rls_transaction from the project’s existing helper module.

As per coding guidelines, “Any query against tenant-scoped models outside a ViewSet ... MUST be wrapped in rls_transaction(tenant_id).”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/src/backend/api/sse/channelmanager.py` at line 64, The call to
`user.is_member_of_tenant(tenant_id)` on line 64 queries tenant-scoped data
outside a ViewSet without RLS protection, violating the project's RLS
enforcement contract. Wrap this membership lookup call in an
`rls_transaction(tenant_id)` context manager to ensure proper tenant-scoped
access control. Additionally, import `rls_transaction` from the project's
existing helper module at the top of the file to make it available for use in
this code path.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@api/src/backend/api/sse/channelmanager.py`:
- Line 64: The call to `user.is_member_of_tenant(tenant_id)` on line 64 queries
tenant-scoped data outside a ViewSet without RLS protection, violating the
project's RLS enforcement contract. Wrap this membership lookup call in an
`rls_transaction(tenant_id)` context manager to ensure proper tenant-scoped
access control. Additionally, import `rls_transaction` from the project's
existing helper module at the top of the file to make it available for use in
this code path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6df24f96-9f96-40c7-89f6-664f09dd4537

📥 Commits

Reviewing files that changed from the base of the PR and between 247bb4e and e9101c6.

�� Files selected for processing (1)
  • api/src/backend/api/sse/channelmanager.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants