trunk-merge/pr-88073/bbb61ef6-4184-4282-8dc3-de768a9c30b6 - #89968
Closed
trunk-io[bot] wants to merge 102 commits into
Closed
trunk-merge/pr-88073/bbb61ef6-4184-4282-8dc3-de768a9c30b6#89968trunk-io[bot] wants to merge 102 commits into
trunk-io[bot] wants to merge 102 commits into
Conversation
Adds AccessCeiling, an org-scoped per-channel cap (channel, optional resource, max_level), with channel_ceiling()/classify_channel() in the access_control facade. APIScopePermission denies write-scoped actions for MCP-channel requests (classified by the MCP server user agent on token auth) when the org caps the channel at viewer, before the wildcard-scope early return so *-scoped tokens are clamped too. Gated on the organization security settings feature. No settings UI yet; rows are the API. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LEer2y9uGMkADPjMfWG8WK
APIScopePermission now only translates DRF vocabulary and applies the verdict; classification, the entitlement gate, row lookup and the denial copy live in ceiling_denial_for_request. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LEer2y9uGMkADPjMfWG8WK
…n class ChannelCeilingPermission joins the mixin stack (including the dangerously_get_permissions branch, like domain enforcement) instead of living inside APIScopePermission. DRF evaluates permission classes with AND semantics, so the wildcard-scope early return can no longer matter, and session-authenticated channels become cappable later. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LEer2y9uGMkADPjMfWG8WK
AccessCeiling becomes SurfaceAccessLimit, channel becomes surface, and the permission class becomes WithinSurfaceLimits, so the vocabulary reads without a glossary: a surface is how the request arrived (MCP now; personal API keys, share links, impersonation later) and a limit is the max level the org allows through it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LEer2y9uGMkADPjMfWG8WK
…lish One statement per sentence, no semicolons, explicit subjects, hedges kept. Also removes the test class docstring per the no-doc-comments-in-tests house rule. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LEer2y9uGMkADPjMfWG8WK
…elds Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LEer2y9uGMkADPjMfWG8WK
resource="*" replaces a nullable resource: a null one column away from max_level="none" read as two different nones. Also drops the nulls_distinct special case from the unique constraint. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LEer2y9uGMkADPjMfWG8WK
…de modules The surface_limits module docstring kept its pre-rewrite wording. Function docstrings now start with a verb, and the user-agent comment loses the mint-time jargon. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LEer2y9uGMkADPjMfWG8WK
…mitPermission The repo's stack convention is the Permission suffix, and the exact model-name prefix makes one grep find the storage, the policy and the enforcement together. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LEer2y9uGMkADPjMfWG8WK
…IDOR rules The coverage check requires every org-scoped model in the semgrep taint rules, so lookups without an organization filter get flagged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LEer2y9uGMkADPjMfWG8WK
The column is not nullable since the wildcard change; the row now takes the "*" default. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LEer2y9uGMkADPjMfWG8WK
… signatures The writes early-return ran before the row lookup, so a disabled surface still served reads, against the model's documented semantics. Adds the regression test and types the permission and policy entry points with concrete request and view classes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LEer2y9uGMkADPjMfWG8WK
Expose list, create, and update operations for error tracking severity rules through the PostHog MCP server. Generated-By: PostHog Desktop Task-Id: 12089906-88c9-4363-aa68-4705d0f4c5f4
The eval report dead-ID guard only matched canonical UUIDs, so opaque session and trace IDs (like chat_thread_*) shipped as dead backticked text. set_title had no guard, and add_section read citations mid-loop so an agent that drafted before citing hit a spurious rejection. Key the guard on the IDs the session handled (its query allowlists) plus UUID shape, and move the check into _validate_agent_output so it covers the title and no longer depends on tool-call order. Generated-By: PostHog Desktop Task-Id: 914d878b-3988-49f0-b605-514ff266dedc
Need to support additional fields to prepare the AWS API GetClusterCredentials and GetCredentials (for Redshift serverless) requests, like user and account id. Those requests are later done in the batch export to obtain temporary Redshift credentials. The changes made in the parent class maintain Liskov substitution: An extra dict fields is added to the integration config, and subclasses can override how we get that extra dict if they wish. New AWS-role based integrations may not fit this model, but I have currently no evidence of this, so we'll just cross that bridge when we get to it. Notice the AWS role used to assume user provided roles is hardcoded to the batch exports role, this can be cleaned up when a different use case arrives. This commit also contains the first instance (that I can recall) of me going for a staticmethod! In my whole career, I've always just used a normal function (modules are namespaces). But this one time it's needed as the implementation of the staticmethod does vary based on subclass, so a single function would not work.
Redshift AWS credentials now require a user parameter as the AWS GetClusterCredentials API requires the user we are obtaining credentials for. Must then check first for AWS credentials to avoid a user parameter matching on the plain credentials class.
AWS AssumeRole requests take a Policy string argument, which doesn't have to be a single JSON object (i.e. a single policy statement), but can actually be a JSON array with multiple policy statements. Support for this is necessary for Redshift, as it does need policy statements targetting multiple resources. It's getting to the point that we should have a separate aws.py module somewhere for all AWS-related destinations, rather than importing from the s3_batch_export.py. But I am leaving that as a TODO for now, as I didn't see an obvious location for this new aws.py.
Redshift batch export now can resolve credentials from integrations. For COPY Redshift, this applies to both the credentials needed to connect to Redshift and the credentials needed to stage the files in the S3 bucket. In follow-up commit, I will tackle the frontend for this, which will likely require TWO integration modals when using COPY.
We always expect the redshift host to end in either redshift.amazonaws.com or redshift-serverless.amazonaws.com. So, we can more strictly parse the redshift host by limiting the number of splits, and matching the entire suffix rather than using a wildcard. This prevents any potential attacks using invalid hosts.
Co-authored-by: Ross <ross@posthog.com>
This adjust some docstrings and moves client input fetching to the block where its used.
Different operations in the Redshift batch export require S3 credentials.\nNow that the s3_client supports refreshing them, we make sure to set the refresh callable whever an s3 client is used
A Redshift COPY query takes a SESSION_TOKEN in its authorization clause.\n We should use the one available in our AWSCredentials, if any.
…tion column Replaces the SurfaceAccessLimit table with Organization.mcp_access_read_only, matching the sibling security settings, per review. The enforcement layer keeps its shape: the policy lives in facade/mcp_access.py and MCPAccessPermission enforces it in every viewset stack. Drops the per-resource wildcard and the none level; read-only is the only cap. The hot-table migration is acknowledged and needs a coordinated deploy. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LEer2y9uGMkADPjMfWG8WK
A non-member probing another project with an MCP-marked write got the policy message before the membership check ran, disclosing that organization's security setting. The cap now runs after TeamMemberAccessPermission and domain enforcement, so non-members get the generic denial. Enforcement is unchanged: DRF combines permission classes with AND semantics, so order only decides which denial message wins. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LEer2y9uGMkADPjMfWG8WK
…ilings # Conflicts: # posthog/migrations/max_migration.txt
…wledgment Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LEer2y9uGMkADPjMfWG8WK
…annotation Master added an import-linter contract while this branch was in flight: facade modules must not import DRF. The permission class moves to presentation/ (exposed via tach), and the facade policy types its request parameter with a structural protocol instead of the DRF Request. Also drops a wrong HttpResponse annotation mypy caught. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LEer2y9uGMkADPjMfWG8WK
The summarizer's facets turn treated any error visible on screen as friction. On product surfaces whose job is to display errors — monitoring, logging, tracing, exception and support views — that inverts the reading: a user successfully finding the error they came for gets recorded as having hit a blocker. Narrow the friction definition so error content the product is displaying is excluded, and friction is reserved for the product failing the user. Generated-By: PostHog Desktop Task-Id: e92dac5c-9cee-4115-aa14-86f7f47240bd
Metabase credential validation refuses to follow redirects as an SSRF guard. When the Instance URL responds with a 3xx, validation returned the raw "Metabase host is not allowed", which tells the user nothing they can act on. Return a message that names the redirect and points at the fix: enter the direct instance URL, then reconnect. Generated-By: PostHog Desktop Task-Id: ae9fe31b-7111-4d9b-893c-3d3ffe64ce00
A user who pastes an HTTP method in front of the URL (for example a "POST https://..." copied from API docs) into the custom source base URL failed validation with "is missing a hostname", even though the URL plainly has one. urlparse reads no host once a method prefix is present. Detect a leading HTTP verb and return a message that names the fix: remove the method and enter just the address. Generated-By: PostHog Desktop Task-Id: ae9fe31b-7111-4d9b-893c-3d3ffe64ce00
…d hold open fences
Plain moves from Alpha to GA and Intercom from Beta to GA. Both meet the liveness and error-rate bars for general availability, so drop their controlled-rollout feature flags to release them to all teams. Generated-By: PostHog Desktop Task-Id: 2bf2cadf-375d-45ef-be5c-ee7dcf773680
trunk-io
Bot
deleted the
trunk-merge/pr-88073/bbb61ef6-4184-4282-8dc3-de768a9c30b6
branch
August 27, 2026 10:05
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This pull request was created and is being managed by Trunk Merge.
This pull request is based on the master branch at SHA d788f6229b9f0f51530ec0e6c7f5a46158df17f5.
See more details about each PR in the batch here:
When CI completes, this pull request will be closed automatically.
Pull Requests Being Tested
This pull request is testing a batch with the changes from pull requests 88073 and 89895 - batching documentation.
Dependencies
This pull request depends on the changes from pull requests 89911, 89686, 87881, 89715, 86299, 89909, 89317, 89919, 86497, and 89836.