Skip to content

trunk-merge/pr-88073/bbb61ef6-4184-4282-8dc3-de768a9c30b6 - #89968

Closed
trunk-io[bot] wants to merge 102 commits into
masterfrom
trunk-merge/pr-88073/bbb61ef6-4184-4282-8dc3-de768a9c30b6
Closed

trunk-merge/pr-88073/bbb61ef6-4184-4282-8dc3-de768a9c30b6#89968
trunk-io[bot] wants to merge 102 commits into
masterfrom
trunk-merge/pr-88073/bbb61ef6-4184-4282-8dc3-de768a9c30b6

Conversation

@trunk-io

@trunk-io trunk-io Bot commented Aug 27, 2026

Copy link
Copy Markdown
Trunk Merge Pull Request Banner

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.

a-lider and others added 30 commits August 20, 2026 12:52
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
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
…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
carlos-marchal-ph and others added 27 commits August 27, 2026 10:39
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
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 trunk-io Bot closed this Aug 27, 2026
@trunk-io
trunk-io Bot deleted the trunk-merge/pr-88073/bbb61ef6-4184-4282-8dc3-de768a9c30b6 branch August 27, 2026 10:05
@trunk-io

trunk-io Bot commented Aug 27, 2026

Copy link
Copy Markdown
Author

Static BadgeStatic BadgeStatic Badge

View Full Report ↗︎Docs

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.

7 participants