Skip to content

fix: security hardening — iframe sandbox, CSP, webhook auth, path traversal - #36

Closed
joshuajerin wants to merge 4 commits into
10xapp:mainfrom
joshuajerin:fix/iframe-sandbox-security
Closed

fix: security hardening — iframe sandbox, CSP, webhook auth, path traversal#36
joshuajerin wants to merge 4 commits into
10xapp:mainfrom
joshuajerin:fix/iframe-sandbox-security

Conversation

@joshuajerin

@joshuajerin joshuajerin commented Apr 2, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Iframe sandbox (CRITICAL): Add sandbox attribute to AI builder preview iframe, removing unrestricted camera/mic/geo access
  • CSP enforcement (HIGH): Promote Content-Security-Policy from report-only to enforced, with correct frame-src for Expo Snack
  • Google webhook verification (HIGH): Add GOOGLE_WEBHOOK_TOKEN config; Gmail verifies ?token= param, Calendar verifies X-Goog-Channel-Token header; watch creation includes the token
  • Sandbox path traversal (HIGH): Replace weak regex sanitization with posixpath.normpath + prefix validation + shlex.quote to prevent ../ traversal in E2B sandbox file browser

Test plan

  • AI builder preview still loads Expo Snack apps correctly with sandbox attribute
  • Verify CSP doesn't block any existing features (check browser console for violations)
  • Set GOOGLE_WEBHOOK_TOKEN env var and verify Gmail/Calendar webhooks accept valid token and reject missing/invalid ones
  • Existing Calendar watches will need renewal to pick up the new token
  • Test sandbox file listing with paths containing ../ — should reject with ValueError
  • Test sandbox file listing with normal paths under /home/user — should work as before

Summary by CodeRabbit

  • Security Improvements

    • Enforced webhook token verification for Gmail and Calendar endpoints
    • Enforced Content Security Policy and expanded script/connect/frame sources
    • Tightened iframe sandbox permissions in the preview component
    • Improved sandbox file path validation and quoting
  • Chores

    • Added a new configuration field for the Google webhook token and made it required where applicable
  • Tests

    • Updated unit tests to include the webhook token in relevant scenarios

The iframe running Expo Snack previews had no sandbox attribute and
granted geolocation/camera/microphone permissions, allowing
AI-generated code full access to the parent origin's storage,
cookies, and navigation.

Add sandbox with minimal permissions (scripts, same-origin, forms,
popups) and remove unnecessary device access grants.
1. CSP enforcement (vercel.json): Promote Content-Security-Policy from
   report-only to enforced. Add frame-src for Expo Snack preview,
   connect-src for PostHog, and wasm-unsafe-eval for Snack SDK.

2. Google webhook verification (webhooks.py, config.py, watch_manager.py):
   Add GOOGLE_WEBHOOK_TOKEN config. Gmail endpoint verifies ?token= query
   param; Calendar endpoint verifies X-Goog-Channel-Token header. Watch
   creation now includes the token so Google sends it back on notifications.

3. Sandbox path traversal fix (sandbox_files.py): Replace weak regex
   sanitization with posixpath.normpath + prefix validation + shlex.quote.
   Paths must resolve under /home/user to prevent ../ traversal.
@coderabbitai

coderabbitai Bot commented Apr 2, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds a new Google webhook token setting and enforces its use for Pub/Sub/Gmail and Calendar webhooks, adds server-side token verification, tightens sandbox path sanitization, changes iframe sandboxing attributes, and updates CSP from report-only to enforced. Tests and subscription setup updated to require the token.

Changes

Cohort / File(s) Summary
Google webhook + config
core-api/api/config.py, core-api/api/routers/webhooks.py, core-api/api/services/syncs/watch_manager.py, core-api/setup_pubsub_subscription.py
Added google_webhook_token setting; new _verify_google_webhook_token(token) helper; Gmail endpoint accepts token query param and Calendar accepts X-Goog-Channel-Token header — both reject missing/invalid tokens (401). Watch manager and Pub/Sub setup include/require the token when creating watches/subscriptions.
Sandbox path sanitization
core-api/api/services/agents/sandbox_files.py
Replaced ad-hoc regex sanitization with _sanitize_sandbox_path() using posixpath.normpath, containment check under /home/user, and shlex.quote; invalid or out-of-root paths now raise ValueError.
Frontend iframe & CSP
core-web/src/components/ai-app-builder/BuilderPreview.tsx, core-web/vercel.json
Iframe allow attribute removed and replaced with sandbox="allow-scripts allow-same-origin allow-forms allow-popups"; Vercel header changed from Content-Security-Policy-Report-Only to enforced Content-Security-Policy, added 'wasm-unsafe-eval' to script-src, added https://*.posthog.com to connect-src, and allowed Snack/Expo origins in frame-src.
Tests
core-api/tests/unit/test_watch_manager_permanent_failures.py
Updated mocked settings to include google_webhook_token="test-token" in calendar watch failure tests.

Sequence Diagram(s)

sequenceDiagram
    participant Google as Google (Pub/Sub / Calendar / Gmail)
    participant Server as App Server (webhooks endpoint)
    participant WatchSvc as WatchManager / PubSub setup
    participant Queue as Work Queue / Processor

    Note over WatchSvc,Google: Create watch / Pub/Sub subscription (includes token)
    WatchSvc->>Google: POST /watches (body includes token)
    Google-->>WatchSvc: 200 OK

    Note over Google,Server: Incoming webhook push
    Google->>Server: POST /api/webhooks/gmail?token=... or POST /api/webhooks/calendar with X-Goog-Channel-Token
    Server->>Server: _verify_google_webhook_token(token)
    alt token valid
        Server->>Queue: enqueue processing job
        Server-->>Google: 200 OK
    else token invalid
        Server-->>Google: 401 Unauthorized
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I tuck a token in my paw so neat,
I trim each path and keep it sweet,
I sandbox frames and watch the gate,
Subscriptions hum and webhooks wait,
A hop, a check — the internet's complete.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 73.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the four main security improvements: iframe sandbox, CSP enforcement, webhook authentication, and path traversal prevention.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

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

🧹 Nitpick comments (3)
core-web/vercel.json (1)

8-8: Narrow connect-src to explicit PostHog hosts.

The CSP currently allows https://*.posthog.com, but the code uses only https://us.i.posthog.com by default (with optional env override via VITE_POSTHOG_HOST). Tightening this to the specific expected host(s) reduces outbound surface in case of XSS.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core-web/vercel.json` at line 8, Update the CSP string in the "value" entry
so connect-src no longer contains the wildcard "https://*.posthog.com"; instead
list the specific PostHog host(s) your app uses (e.g., the default
"https://us.i.posthog.com" and any alternate host you support via
VITE_POSTHOG_HOST) so the connect-src directive explicitly allows only those
exact origins; locate and edit the connect-src portion of the policy string in
the current value to replace "https://*.posthog.com" with the exact host(s) you
intend to permit.
core-api/api/config.py (1)

187-189: Add fail-fast validation for webhook token configuration.

google_webhook_token defaults to empty, but webhook handlers now reject requests when it is unset. Consider validating this at startup (e.g., when Google webhook features are enabled) to avoid silent runtime breakage.

Suggested guard
 class Settings(BaseSettings):
@@
     google_webhook_token: str = ""
@@
     `@model_validator`(mode="after")
     def validate_token_encryption_settings(self):
@@
         return self
+
+    `@model_validator`(mode="after")
+    def validate_google_webhook_settings(self):
+        if self.google_pubsub_topic and not self.google_webhook_token:
+            raise ValueError(
+                "GOOGLE_WEBHOOK_TOKEN must be set when GOOGLE_PUBSUB_TOPIC is configured"
+            )
+        return self
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core-api/api/config.py` around lines 187 - 189, The config currently sets
google_webhook_token = "" which leads to runtime rejections; add a fail-fast
validation that checks google_webhook_token at startup (for example in the
Config class __post_init__ or in the existing application startup/init function
that enables Google webhook features) and raise a clear exception or exit the
process if the token is empty when webhook handling is enabled; reference the
google_webhook_token setting and the startup/init code path that enables Google
webhook features so the app fails fast with an explicit error message rather
than silently breaking at request time.
core-api/api/routers/webhooks.py (1)

63-63: Use Annotated for FastAPI dependency parameters to satisfy tooling.

Switching dependency params to Annotated[...] follows modern FastAPI best practices (0.95.0+) and clears Sonar violations. This also improves editor support and type checking.

Proposed refactor
-from typing import Optional
+from typing import Optional, Annotated
@@
-async def gmail_webhook(request: Request, token: Optional[str] = Query(None)):
+async def gmail_webhook(
+    request: Request,
+    token: Annotated[Optional[str], Query()] = None
+):
@@
-    x_goog_message_number: Optional[str] = Header(None),
-    x_goog_channel_token: Optional[str] = Header(None),
+    x_goog_message_number: Annotated[Optional[str], Header()] = None,
+    x_goog_channel_token: Annotated[Optional[str], Header()] = None,
 ):

Also applies to: 177-178

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core-api/api/routers/webhooks.py` at line 63, The gmail_webhook dependency
parameter should use typing.Annotated to satisfy FastAPI tooling: change the
signature in gmail_webhook from token: Optional[str] = Query(None) to token:
Annotated[Optional[str], Query(None)] and similarly update the other dependency
parameters referenced around lines 177-178 to use Annotated[...] with their
Query/Depends markers; also add the necessary Annotated import (e.g., from
typing import Annotated) alongside existing Optional imports so type checkers
and Sonar stop flagging these parameters.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@core-api/api/routers/webhooks.py`:
- Around line 89-92: The webhook handlers currently return a JSON body with
"Unauthorized" while sending HTTP 200; update the logic around
_verify_google_webhook_token to return proper HTTP auth status codes (raise
fastapi.HTTPException(status_code=401, detail="Unauthorized") or return a
Response with status_code=401/403) instead of returning {"status":"error",...} —
apply this change in the Gmail webhook verification branch where
_verify_google_webhook_token(token) is checked and the other identical block
later in the file so invalid tokens produce a 401/403 HTTP response and not a
200 OK.
- Around line 63-64: The Gmail webhook now expects a query parameter token but
the Pub/Sub subscription setup still constructs the push endpoint without it,
causing rejected notifications; update the setup script to include the token
when building the push endpoint (append ?token=<YOUR_VERIFICATION_TOKEN> or
proper URL-encoding) so the generated push_endpoint matches what the
gmail_webhook handler expects, or alternatively relax gmail_webhook's validation
to accept Pub/Sub calls without the token; locate the gmail_webhook function and
the push_endpoint variable in the setup_pubsub_subscription script and make the
token inclusion/validation consistent between them.

In `@core-api/api/services/agents/sandbox_files.py`:
- Around line 6-8: Remove the unused import `re` from the top of
sandbox_files.py to satisfy the linter (Ruff F401); locate the import line that
currently reads "import posixpath, import re, import shlex" (or separate
imports) and delete the `re` entry so only the used modules (e.g., posixpath and
shlex) remain imported.

In `@core-api/api/services/syncs/watch_manager.py`:
- Around line 265-267: Both code paths that optionally attach
settings.google_webhook_token to request_body (the conditional around
request_body['token']) must fail fast when the token is unset because the
webhook endpoint now requires a valid token; update the logic so that wherever
you currently have "if settings.google_webhook_token: request_body['token'] =
settings.google_webhook_token" you instead check for a truthy
settings.google_webhook_token and if missing log an error and raise/return an
explicit failure (e.g., raise an exception or return an error result) so the
Calendar watch creation is aborted rather than creating an unusable
subscription; apply this change to the two places that build request_body (the
branch that currently sets request_body['token'] and the second occurrence
further down).

---

Nitpick comments:
In `@core-api/api/config.py`:
- Around line 187-189: The config currently sets google_webhook_token = "" which
leads to runtime rejections; add a fail-fast validation that checks
google_webhook_token at startup (for example in the Config class __post_init__
or in the existing application startup/init function that enables Google webhook
features) and raise a clear exception or exit the process if the token is empty
when webhook handling is enabled; reference the google_webhook_token setting and
the startup/init code path that enables Google webhook features so the app fails
fast with an explicit error message rather than silently breaking at request
time.

In `@core-api/api/routers/webhooks.py`:
- Line 63: The gmail_webhook dependency parameter should use typing.Annotated to
satisfy FastAPI tooling: change the signature in gmail_webhook from token:
Optional[str] = Query(None) to token: Annotated[Optional[str], Query(None)] and
similarly update the other dependency parameters referenced around lines 177-178
to use Annotated[...] with their Query/Depends markers; also add the necessary
Annotated import (e.g., from typing import Annotated) alongside existing
Optional imports so type checkers and Sonar stop flagging these parameters.

In `@core-web/vercel.json`:
- Line 8: Update the CSP string in the "value" entry so connect-src no longer
contains the wildcard "https://*.posthog.com"; instead list the specific PostHog
host(s) your app uses (e.g., the default "https://us.i.posthog.com" and any
alternate host you support via VITE_POSTHOG_HOST) so the connect-src directive
explicitly allows only those exact origins; locate and edit the connect-src
portion of the policy string in the current value to replace
"https://*.posthog.com" with the exact host(s) you intend to permit.
🪄 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: CHILL

Plan: Pro

Run ID: 3e8a899b-0cad-4692-8974-445ff6db69f3

📥 Commits

Reviewing files that changed from the base of the PR and between f50fda1 and 64404cf.

📒 Files selected for processing (6)
  • core-api/api/config.py
  • core-api/api/routers/webhooks.py
  • core-api/api/services/agents/sandbox_files.py
  • core-api/api/services/syncs/watch_manager.py
  • core-web/src/components/ai-app-builder/BuilderPreview.tsx
  • core-web/vercel.json

Comment thread core-api/api/routers/webhooks.py
Comment thread core-api/api/routers/webhooks.py Outdated
Comment thread core-api/api/services/agents/sandbox_files.py
Comment thread core-api/api/services/syncs/watch_manager.py Outdated
- Remove unused `re` import (ruff F401 lint failure)
- Return HTTP 401 instead of 200 on invalid webhook tokens
- Fail fast in watch creation when GOOGLE_WEBHOOK_TOKEN is unset
- Include token in Pub/Sub setup script push endpoint
- Add google_webhook_token to test mocks to fix 2 failing tests

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

🧹 Nitpick comments (2)
core-api/api/routers/webhooks.py (1)

62-63: SonarCloud: Cognitive complexity and type hint style.

Static analysis flags:

  1. Cognitive complexity 17 > 15 — the nested try/except and inline queue fallback contribute. Consider extracting the queue-enqueue logic into a helper.
  2. Use Annotated type hints — FastAPI recommends Annotated[str, Query(...)] over Query(...) as default value for better tooling support.
  3. Document 401 in responses — add responses={401: {"description": "Invalid or missing token"}} to the decorator.

These are style/documentation improvements, not blockers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core-api/api/routers/webhooks.py` around lines 62 - 63, The gmail_webhook
endpoint has high cognitive complexity due to nested try/except and inline queue
fallback — extract the queue-enqueue and fallback logic into a separate helper
(e.g., enqueue_gmail_webhook or enqueue_webhook_event) and move the try/except
blocks into that helper so gmail_webhook becomes a simple orchestrator; change
the token parameter annotation to use Annotated[str, Query(None)] instead of
Query(...) for FastAPI typing; and update the `@router.post` decorator on
gmail_webhook to include responses={401: {"description": "Invalid or missing
token"}} to document the 401 case.
core-api/setup_pubsub_subscription.py (1)

54-54: Consider masking the token in printed output.

The full endpoint URL (including the secret token) is printed at Line 54 and Line 100. While this is a manual setup script, printing secrets can lead to accidental exposure in terminal history or CI logs if someone repurposes this script.

Suggested redaction
-    print(f"🌐 Push Endpoint: {push_endpoint}")
+    # Redact token in output
+    safe_endpoint = f"{webhook_url}/api/webhooks/gmail?token=****"
+    print(f"🌐 Push Endpoint: {safe_endpoint}")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core-api/setup_pubsub_subscription.py` at line 54, The script currently
prints the full push_endpoint (variable push_endpoint) which includes a secret
token; update both print sites that output push_endpoint to redact the token
before printing by parsing the URL/string and replacing the token value with a
masked string (e.g., show only last 4 chars or "<redacted>") so logs never
contain the secret; locate usages of push_endpoint (the print at Line 54 and the
later print around Line 100) and replace direct printing with a masked
representation generated by a short helper (e.g.,
mask_token_from_url(push_endpoint)) or inline parsing to preserve host/path but
hide the token.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@core-api/api/routers/webhooks.py`:
- Around line 87-91: Move the token verification out of the broad try/except so
raised HTTPException is not swallowed: for both webhook handlers (the Gmail
handler that calls _verify_google_webhook_token and the calendar_webhook
handler), perform the token check and raise HTTPException(status_code=401)
before entering the try block that catches Exception; this ensures the
authentication error propagates as a 401 instead of being caught and converted
into a 200 error response.

---

Nitpick comments:
In `@core-api/api/routers/webhooks.py`:
- Around line 62-63: The gmail_webhook endpoint has high cognitive complexity
due to nested try/except and inline queue fallback — extract the queue-enqueue
and fallback logic into a separate helper (e.g., enqueue_gmail_webhook or
enqueue_webhook_event) and move the try/except blocks into that helper so
gmail_webhook becomes a simple orchestrator; change the token parameter
annotation to use Annotated[str, Query(None)] instead of Query(...) for FastAPI
typing; and update the `@router.post` decorator on gmail_webhook to include
responses={401: {"description": "Invalid or missing token"}} to document the 401
case.

In `@core-api/setup_pubsub_subscription.py`:
- Line 54: The script currently prints the full push_endpoint (variable
push_endpoint) which includes a secret token; update both print sites that
output push_endpoint to redact the token before printing by parsing the
URL/string and replacing the token value with a masked string (e.g., show only
last 4 chars or "<redacted>") so logs never contain the secret; locate usages of
push_endpoint (the print at Line 54 and the later print around Line 100) and
replace direct printing with a masked representation generated by a short helper
(e.g., mask_token_from_url(push_endpoint)) or inline parsing to preserve
host/path but hide the token.
🪄 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: CHILL

Plan: Pro

Run ID: 032c36f7-4353-4b6c-853b-456b7d0702f3

📥 Commits

Reviewing files that changed from the base of the PR and between 64404cf and a092745.

📒 Files selected for processing (5)
  • core-api/api/routers/webhooks.py
  • core-api/api/services/agents/sandbox_files.py
  • core-api/api/services/syncs/watch_manager.py
  • core-api/setup_pubsub_subscription.py
  • core-api/tests/unit/test_watch_manager_permanent_failures.py
✅ Files skipped from review due to trivial changes (1)
  • core-api/tests/unit/test_watch_manager_permanent_failures.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • core-api/api/services/syncs/watch_manager.py

Comment thread core-api/api/routers/webhooks.py Outdated
… 401

The outer try/except catches all Exceptions (including HTTPException)
and returns 200 OK. Auth verification must happen before this block
so the 401 actually reaches the client.
@sonarqubecloud

sonarqubecloud Bot commented Apr 2, 2026

Copy link
Copy Markdown

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

🧹 Nitpick comments (2)
core-api/api/routers/webhooks.py (2)

92-168: Consider decomposing gmail_webhook to reduce complexity.

The handler on Line 92-168 still bundles parsing, validation, queueing, fallback, and error handling in one function. Extracting these into helpers would improve readability and should clear the cognitive complexity gate.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core-api/api/routers/webhooks.py` around lines 92 - 168, The gmail_webhook
handler is doing parsing, validation, queueing and inline processing in one
large block; split it into small helpers: extract JSON parsing/validation into a
function parse_pubsub_message(request) that returns (email_address, history_id)
or raises, move the QStash enqueue logic into
enqueue_gmail_sync(connection_lookup_client, email_address, history_id) (use
queue_client and get_service_role_client inside), and keep inline processing via
a separate function process_gmail_notification_thread(email_address, history_id)
that calls asyncio.to_thread(process_gmail_notification,...). Replace the big
body in gmail_webhook with sequential calls to parse_pubsub_message,
enqueue_gmail_sync (fallback to process_gmail_notification_thread on
False/exception), and centralize error logging/returns so behavior remains
identical while reducing cognitive complexity.

63-63: Use Annotated for FastAPI dependency parameters.

Lines 63, 174-178 use legacy = Query(None) / = Header(None) style. Switching to Annotated[...] improves typing clarity and aligns with current FastAPI conventions.

♻️ Proposed refactor
-from typing import Optional
+from typing import Annotated, Optional
@@
-async def gmail_webhook(request: Request, token: Optional[str] = Query(None)):
+async def gmail_webhook(
+    request: Request,
+    token: Annotated[Optional[str], Query()] = None,
+):
@@
-async def calendar_webhook(
-    request: Request,
-    x_goog_channel_id: Optional[str] = Header(None),
-    x_goog_resource_id: Optional[str] = Header(None),
-    x_goog_resource_state: Optional[str] = Header(None),
-    x_goog_message_number: Optional[str] = Header(None),
-    x_goog_channel_token: Optional[str] = Header(None),
+async def calendar_webhook(
+    request: Request,
+    x_goog_channel_id: Annotated[Optional[str], Header()] = None,
+    x_goog_resource_id: Annotated[Optional[str], Header()] = None,
+    x_goog_resource_state: Annotated[Optional[str], Header()] = None,
+    x_goog_message_number: Annotated[Optional[str], Header()] = None,
+    x_goog_channel_token: Annotated[Optional[str], Header()] = None,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core-api/api/routers/webhooks.py` at line 63, Replace legacy dependency
parameter syntax with Annotated-based typing: import Annotated and update the
gmail_webhook signature (and other handlers around lines 174-178) to declare
Query and Header params as Annotated[...] instead of using "= Query(None)" or "=
Header(None)". For example, change parameters like token: Optional[str] =
Query(None) to token: Annotated[Optional[str], Query(None)] and similarly
convert header parameters (e.g., any params using Header(None)) to
Annotated[Optional[str], Header(None)] so the function signatures (gmail_webhook
and the mentioned header-handling endpoints) use modern FastAPI typing
conventions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@core-api/api/routers/webhooks.py`:
- Around line 62-63: The route decorators for gmail_webhook and calendar_webhook
(the `@router.post`(...) endpoints named gmail_webhook and calendar_webhook) are
missing a 401 response declaration; update their `@router.post` decorators to
include responses={"401": {"description": "Unauthorized"}} (or equivalent
OpenAPI schema) so the OpenAPI docs reflect the HTTPException(status_code=401)
raised in those handlers and generated clients see the 401 response.

---

Nitpick comments:
In `@core-api/api/routers/webhooks.py`:
- Around line 92-168: The gmail_webhook handler is doing parsing, validation,
queueing and inline processing in one large block; split it into small helpers:
extract JSON parsing/validation into a function parse_pubsub_message(request)
that returns (email_address, history_id) or raises, move the QStash enqueue
logic into enqueue_gmail_sync(connection_lookup_client, email_address,
history_id) (use queue_client and get_service_role_client inside), and keep
inline processing via a separate function
process_gmail_notification_thread(email_address, history_id) that calls
asyncio.to_thread(process_gmail_notification,...). Replace the big body in
gmail_webhook with sequential calls to parse_pubsub_message, enqueue_gmail_sync
(fallback to process_gmail_notification_thread on False/exception), and
centralize error logging/returns so behavior remains identical while reducing
cognitive complexity.
- Line 63: Replace legacy dependency parameter syntax with Annotated-based
typing: import Annotated and update the gmail_webhook signature (and other
handlers around lines 174-178) to declare Query and Header params as
Annotated[...] instead of using "= Query(None)" or "= Header(None)". For
example, change parameters like token: Optional[str] = Query(None) to token:
Annotated[Optional[str], Query(None)] and similarly convert header parameters
(e.g., any params using Header(None)) to Annotated[Optional[str], Header(None)]
so the function signatures (gmail_webhook and the mentioned header-handling
endpoints) use modern FastAPI typing conventions.
🪄 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: CHILL

Plan: Pro

Run ID: 29d71247-7774-4e52-8d15-401f15c9a47a

📥 Commits

Reviewing files that changed from the base of the PR and between a092745 and 3aac062.

📒 Files selected for processing (1)
  • core-api/api/routers/webhooks.py

Comment thread core-api/api/routers/webhooks.py
@joshuajerin

Copy link
Copy Markdown
Contributor Author

@benwxng why'd u close it

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.

1 participant