Repository navigation
Conversation
…lusions Unfinished: the red/green proof runs and the OpenAPI regeneration were interrupted by a devbox rebalance. Tests are written but not yet run green; generated OpenAPI output is not regenerated. Continue test-first. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ender endpoint Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…equire write scope Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…quest_access comment Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Merging to
After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here |
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
📒 Files selected for processing (2)
Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 10 remain after this review. 📝 WalkthroughWalkthroughThe change adds backend provisioning for managed sandbox email integrations and prevents users from verifying, editing, or deleting them. Node email sends can use a configured sandbox SES route and fixed sender identity. Disabled sends are skipped, and sandbox sends bypass team sending-budget claims. Tests cover provisioning, send behavior, and selected lifecycle and reputation operations. Priority: ➖ Normal Merge Risk: 🟡 Moderate · up to Disabled sandbox sends may still error or miss skip accounting and the switch-off event. Resolve those paths before merging unless their behavior is explicitly accepted. Security Architecture ReviewSecurity architecture risk: 🟡 Moderate · up to The sender is disabled by default, and project ownership and fixed-identity controls limit exposure. However, enabling it before independent recipient, volume and pause safeguards are deployed would allow tenant-controlled traffic through a shared sending identity without the ordinary project sending budget. Production enablement was not verified. Retained concerns
Security review detailsSecurity Blast Radius
Security Findings and Attack Paths
Trust Boundaries and Controls
Resilience and Maintainability Implications
Hardening Proposals
🚥 Pre-merge checks | ✅ 1✅ Passed checks (1 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Note
Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.
🟡 Other comments (2)
nodejs/src/cdp/services/messaging/email.service.ts-482-497 (1)
482-497: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winSandbox-disabled check runs after
resolveFromSender, which can throw first.For a disabled switch,
resolveFromSender(Line 482) throws if the integration is unverified or lacksname. The send then fails with an error instead of the intendedskippedresult. Move theisSandbox && !enabledgate beforeresolveFromSender.Proposed fix
- const from = this.resolveFromSender(integration, params.from, addLog) - if (isSandbox && !this.sandboxSender?.config.enabled) { ... return result } + const from = this.resolveFromSender(integration, params.from, addLog)posthog/api/integration.py-2447-2462 (1)
2447-2462: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winRequire admin access before refreshing an existing sandbox sender.
When an existing sender’s config differs,
ensure_sandbox_email_sendercan overwrite it and reload integrations. The action uses the create-level permission chain, so a project member withintegration:writeaccess can make this edit without the admin access required byIntegrationManagementPermission. Keep member-level access for first-time creation, but require the edit permission before refreshing an existing row.
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Repository: PostHog/posthog/.coderabbit.yaml
- Review profile: QUIET
- Plan: Enterprise
- Run ID:
55c3e128-8522-4e68-ba03-e7f7cda9b08c
⛔ Files ignored due to path filters (1)
products/integrations/frontend/generated/api.tsis excluded by!**/generated/**
📒 Files selected for processing (19)
docs/internal/workflows-email-sending-tiers.mdnodejs/src/cdp/cdp-services.tsnodejs/src/cdp/config.tsnodejs/src/cdp/services/messaging/email.service.test.tsnodejs/src/cdp/services/messaging/email.service.tsnodejs/src/cdp/services/messaging/sandbox-email-sender.tsnodejs/src/cdp/types.tsposthog/api/integration.pyposthog/models/integration/__init__.pyposthog/models/integration/email.pyposthog/settings/ses.pyproducts/integrations/mcp/tools.yamlproducts/workflows/backend/facade/api.pyproducts/workflows/backend/facade/contracts.pyproducts/workflows/backend/presentation/views/hog_flow.pyproducts/workflows/backend/services/sandbox_sender.pyproducts/workflows/backend/test/test_sandbox_sender_lifecycle.pyproducts/workflows/backend/tests/api/test_email_reputation.pyproducts/workflows/backend/tests/api/test_sandbox_sender.py
Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 9 remain after this review.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to GitHub limitations.
🟠 Major · Mark suspended sends as skipped. · email.service.ts:485
nodejs/src/cdp/services/messaging/email.service.ts:485
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winMark suspended sends as skipped.
When suspension returns from
executeSendEmail, it only pushes{ success: false }and leaves bothskippedanddeliveredToRecipientunset.hog_function.tstherefore enters the billing branch:!functionResult.skippedis true, andfunctionResult.deliveredToRecipient !== falseis also true. This can bill an email that was not sent.Set
result.skippedin the suspension return. Keep suspension handling before the disabled sandbox-switch branch so it continues to recordemail_suspended, rather than incorrectly reportingswitch_off.🐛 Suggested fix
result.invocation.state.vmState?.stack.push({ success: false }) + result.skipped = true return resultAdd coverage for a suspended sandbox team with the sandbox switch disabled.
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Repository: PostHog/posthog/.coderabbit.yaml
- Review profile: QUIET
- Plan: Enterprise
- Run ID:
635d848d-02f4-4cc2-916c-2aeafeb16e52
📒 Files selected for processing (2)
nodejs/src/cdp/services/messaging/email.service.test.tsnodejs/src/cdp/services/messaging/email.service.ts
Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 10 remain after this review.
There was a problem hiding this comment.
Note
Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.
🟡 Other comments (1)
nodejs/src/cdp/services/messaging/email.service.ts-497-497 (1)
497-497: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winUse the persisted sandbox sender address for SES.
The backend stores a non-empty
integration.config.email, andresolveFromSenderalready validates it. The sandbox branch ignores that address and usesSES_SANDBOX_FROM_ADDRESS, whose Node default is empty. When the sandbox sender is enabled with that setting empty,sendEmailWithSEScan pass an empty sender and feedback address to SES, which may reject the send. Use the persisted address:🐛 Suggested fix
if (integration.config.provider === 'sandbox') { - return { email: this.sandboxSender?.config.fromAddress ?? '', name: integration.config.name } + return { email: integration.config.email, name: integration.config.name } }
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Repository: PostHog/posthog/.coderabbit.yaml
- Review profile: QUIET
- Plan: Enterprise
- Run ID:
621ad070-3440-4603-bb7a-e7cefddbba12
📒 Files selected for processing (2)
nodejs/src/cdp/services/messaging/email.service.test.tsnodejs/src/cdp/services/messaging/email.service.ts
Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 8 remain after this review.
|
This PR hasn't seen activity in a week! Should it be merged, closed, or further worked on? If you want to keep it open, please remove the |
Warning
Merge risk: high (qa-swarm danger rubric)
Problem
Teams can provision a sandbox sender, but their workflow and test emails cannot reach the email provider through it.
Stacked on #111368. This layer starts at
72ded8d9180; GitHub shows the cumulative fork diff because native stacks cannot cross repositories.Closes Silthus#232.
Changes
Before:
flowchart LR Email[Workflow or test email] --> Own[Own sender] Own --> SES[Team SES route] Email --> Sandbox[Sandbox sender] Sandbox --> Missing[No sandbox route] classDef phYellow fill:#f9bd2b,stroke:#f9bd2b,color:#000; classDef phBlue fill:#1d4aff,stroke:#1d4aff,color:#fff; classDef phRed fill:#f54e00,stroke:#f54e00,color:#fff; class Email,Missing phYellow; class Own,Sandbox phBlue; class SES phRed;After:
flowchart LR Email[Workflow or test email] --> Own[Own sender] Own --> SES[Team SES route] Email --> Sandbox[Sandbox sender] Sandbox --> Switch[Global sending switch] Switch -->|off| Skip[Skipped send] Switch -->|on| Identity[Fixed identity and footer] Identity --> Validate[Validate final HTML] Validate -->|valid| SandboxSES[Sandbox SES route] Validate -->|invalid| Error[Template error before delivery] classDef phYellow fill:#f9bd2b,stroke:#f9bd2b,color:#000; classDef phBlue fill:#1d4aff,stroke:#1d4aff,color:#fff; classDef phRed fill:#f54e00,stroke:#f54e00,color:#fff; class Email,Skip,Error phYellow; class Own,Sandbox,Switch,Identity,Validate phBlue; class SES,SandboxSES phRed;The worker's new settings default to:
Warning
Keep the sending switch off until recipient membership checks, sandbox caps and sandbox pause handling are deployed. This PR provides routing, not those policies.
How did you test this code?
Test rationale: Cases at
EmailService.executeSendEmailextend the existing own-sender and accepted-footer tests. Rejection cases need distinct assertions for errors and absent delivery side effects. Real Postgres and Valkey exercise budgets; only external SES and analytics are mocked.Local verification and observed output
Run from the repository root:
All 145 tests at the public send seam passed, including five unchanged own-sender snapshots. Scoped ESLint/Oxlint and the real pre-push checks passed.
Captured public-seam output included 19 accepted messages and six rejected messages with zero SES calls, saved assets or acceptance analytics. Native T3 browser checks rendered the captured SES HTML with a visible footer and contrasting background. Safe invented-data screenshots remain local because fork permissions prevent asset upload.
No real SES delivery or DNS changes were attempted. There are no web UI changes.
CI remains incomplete: Node.js CI and other fork workflows require maintainer approval.
The ancillary client-library reviewer workflow failed at startup with no jobs or logs. GitHub denied its rerun for insufficient admin rights.
Release status
Creation stays behind
workflows-sandbox-sender; the send path uses the separate default-off global switch.Automatic notifications
Docs update
Updated the existing email sending tiers guide with the sandbox budget exclusion and rollout dependency. No existing document covers footer validation; no new document was added.
🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Agent: Codex, GPT-6.1 Sol
Skills:
/tdd,/writing-tests,/writing-user-facing-copy,/ingestion-pipeline-doctor-nodejs,/hogli,/running-ci-preflight,/stacking-prs,/writing-pr-descriptions,/writing-voice,/unslop, and/qa-swarm.The duplicate search found the parent sandbox provisioning PR, which does not add this send route. The fixtures use invented
example.comaddresses.The prior four-round QA cycle ended blocked on a footer failure. A separately authorized follow-up validates final output, rejects empty content and closes a Redis fixture leak.
Follow-up QA completed three rounds. The final cold panel used six GPT-6.1 Sol lenses and Claude Sonnet 5.5 on
0007a4f79c7.After triage, the final round has no new fix or call. The shared preheader helper's existing replacement-token behavior remains a separate follow-up.
CodeRabbit CLI is signed out, and remote reviews remain paused after an older head. GitHub denied PR assignment and screenshot upload for the fork author.