---
title: "The Vendor Was Out of Scope. The Trust Boundary Was Not."
description: "A third-party platform may be excluded from testing while the customer-controlled trust decisions around it remain assessable: identity claims, callbacks, webhooks, custom domains, delivery paths, and data flows."
date: 2026-09-09
author: Sevban Dönmez (@jankesec)
canonical: https://jankesec.com/posts/third-party-trust-boundary/
---

## Third-party trust in 60 seconds

A modern application rarely owns its whole user journey. An identity provider authenticates the
user. A payment platform sends events. A support system stores conversations. A CDN terminates
traffic. Object storage serves files. Analytics and error telemetry receive application data.

Those providers are part of the architecture, but they are not automatically part of the
penetration-test scope. The useful question is not “can we test the vendor?” It is:

> What customer-controlled decision turns a provider message, identity, hostname, file, or event
> into trusted application state?

That decision may live in the customer’s callback handler, tenant configuration, DNS, role map,
webhook verifier, cache rule, origin policy, or support workflow. The provider platform can remain
strictly excluded while those customer-owned controls are examined safely.

This is the distinction:

- **map the provider** as an architectural dependency;
- **identify the trust contract** at the integration boundary;
- **obtain written approval** for the customer-controlled side;
- **test the smallest meaningful behavior** without probing provider infrastructure;
- **report provider observations as context**, not as findings against a system you were not
  authorized to assess.

“Third party” does not mean “irrelevant.” It means the test needs a better boundary model.

## Split one integration into three control planes

An integration that looks like one box on an architecture diagram usually contains three very
different control planes.

**Provider platform.** The multi-tenant service, its control plane, employees, network ranges,
other tenants, global APIs, and provider-owned identity. This is normally excluded unless the
provider itself has granted explicit permission.

**Customer tenant and configuration.** Redirect URI registrations, SSO connections, signing
secrets, webhook destinations, custom hostnames, origin mappings, storage policies, support roles,
and data-retention settings. The customer may own or administer these choices even though the
provider hosts them. Testing still requires the Rules of Engagement to name the tenant, permitted
accounts, actions, and stop conditions.

**Customer application trust decision.** The code and policy that consume the provider’s output:
which issuer is accepted, which event changes an order, which hostname reaches an origin, which
support role can impersonate a user, or which object becomes publicly retrievable. This is often
the most valuable and least ambiguous place to test.

<figure class="diagram">
<svg viewBox="0 0 700 370" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="A three-plane model separates the excluded provider platform from the customer tenant configuration and customer application trust decisions that may be approved for testing.">
<defs>
<marker id="tptb-a" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto"><path d="M0,0 L8,4 L0,8 z" class="dg-arrow" /></marker>
</defs>
<rect x="18" y="34" width="196" height="254" rx="10" class="dg-box" />
<text x="38" y="64" class="dg-label">provider platform</text>
<text x="38" y="88" class="dg-crit">EXCLUDED BY DEFAULT</text>
<text x="38" y="124" class="dg-muted">multi-tenant control plane</text>
<text x="38" y="148" class="dg-muted">provider network · staff</text>
<text x="38" y="172" class="dg-muted">other tenants · global API</text>
<line x1="232" y1="24" x2="232" y2="304" class="dg-dashed" />
<text x="202" y="328" class="dg-crit">STOP</text>
<rect x="254" y="34" width="196" height="254" rx="10" class="dg-box-accent" />
<text x="274" y="64" class="dg-label">customer tenant</text>
<text x="274" y="88" class="dg-accent">DECISION REQUIRED</text>
<text x="274" y="124" class="dg-muted">redirects · keys · roles</text>
<text x="274" y="148" class="dg-muted">webhooks · hostnames</text>
<text x="274" y="172" class="dg-muted">origin · retention policy</text>
<rect x="486" y="34" width="196" height="254" rx="10" class="dg-box-accent" />
<text x="506" y="64" class="dg-label">customer application</text>
<text x="506" y="88" class="dg-accent">LIKELY TEST PLANE</text>
<text x="506" y="124" class="dg-muted">claim validation</text>
<text x="506" y="148" class="dg-muted">event admission</text>
<text x="506" y="172" class="dg-muted">authorization · state</text>
<line x1="214" y1="218" x2="252" y2="218" class="dg-line" marker-end="url(#tptb-a)" />
<line x1="450" y1="218" x2="484" y2="218" class="dg-line" marker-end="url(#tptb-a)" />
<text x="268" y="250" class="dg-mono">configuration</text>
<text x="506" y="250" class="dg-mono">consumption</text>
<text x="18" y="356" class="dg-mono">ARCHITECTURE CONTEXT ≠ TARGET AUTHORIZATION</text>
</svg>
<figcaption>The provider, the customer’s tenant, and the consuming application are related but not interchangeable. A test plan must state exactly which plane is approved.</figcaption>
</figure>

This split prevents two opposite mistakes. The unsafe mistake is to discover a vendor and begin
scanning its addresses or testing its general login. The incomplete mistake is to mark the entire
integration “out of scope” and ignore the customer’s acceptance logic. Good methodology avoids
both.

## Build a trust-boundary inventory, not a vendor list

A list of brand names—Okta, Stripe, Cloudflare, Zendesk, Sentry—does not yet describe risk. Record
the relationship as a directed flow with an object and an acceptance rule.

| Trust plane             | Inbound object or signal          | Customer-controlled decision                                        |
| ----------------------- | --------------------------------- | ------------------------------------------------------------------- |
| Identity                | Code, token, assertion, claims    | Which issuer, audience, tenant, subject, and role are accepted?     |
| Events and webhooks     | Signed request, event ID, payload | Is it authentic, current, unique, expected, and safe to process?    |
| Custom domain and edge  | Hostname, TLS request, cache key  | Which tenant and origin does the name select?                       |
| Storage and delivery    | File, URL, metadata, content type | Who may publish, retrieve, replace, or transform the object?        |
| Support and operations  | Ticket, role, impersonation act   | Which support action crosses into customer or administrative state? |
| Analytics and telemetry | Identifier, event, error context  | What data leaves, how is it scoped, and who can query it?           |

For each flow, capture the provider, customer tenant identifier, customer endpoint, data class,
direction, authentication mechanism, authorization rule, replay behavior, state change, owner,
scope state, and evidence. If one of those fields is unknown, that is a question—not permission to
guess through active testing.

The inventory should also distinguish discovery evidence from validation evidence. A CNAME,
JavaScript SDK, response header, privacy-policy subprocessor, or login redirect can establish that
a relationship probably exists. It cannot prove how the integration is configured internally or
authorize testing of it.

## Identity: test the handoff, not the identity provider

Federated login creates two security boundaries. The provider proves something about a user; the
customer application decides what that proof means locally. The second boundary is where a small
configuration error can become a customer account or role.

Start by mapping the normal journey with approved test accounts: application login, authorization
request, provider authentication, callback, token exchange, local session creation, role mapping,
logout, and reauthentication. Record the domains and parameters without collecting real-user
tokens or testing other tenants.

The validation questions are precise:

- Does the client accept only the expected issuer and authorization server?
- Is the token intended for this client and resource audience?
- Does the callback URI match the registered destination exactly?
- Are `state`, `nonce`, and PKCE bound to the initiating browser transaction where applicable?
- Can an identity from the wrong organisation or tenant become a local user?
- Are group, email-domain, or role claims converted into privileges only through an explicit map?
- Does a disabled or downgraded provider identity retain a customer session or local role?

[OpenID Connect Core](https://openid.net/specs/openid-connect-core-1_0-18.html) defines validation
requirements around issuer, audience, signature, nonce, and registered redirect URIs. The current
[OAuth 2.0 Security Best Current Practice](https://www.rfc-editor.org/rfc/rfc9700.html) requires
exact redirect matching in the general case, recommends authorization code protections such as
PKCE, and describes issuer and audience defenses against mix-up and token replay.

These specifications turn “test SSO” into observable contracts. A provider-hosted login page is
not the target. The target is the approved customer application’s decision to exchange a specific
provider response for a local identity and authority.

A safe negative test uses accounts, clients, and tenants created for the engagement. It changes
one expected property at a time and observes whether the customer application rejects the result.
It does not spray passwords, register lookalike tenants, enumerate provider customers, or submit
payloads to provider infrastructure outside an approved test workflow.

## Webhooks: authenticity is only the first gate

Webhook security is often reduced to “verify the signature.” That is necessary, but a valid
signature proves only what the configured signing scheme covers. The receiving application still
needs to decide whether the event is current, unique, expected for this endpoint, associated with
the correct customer object, and permitted to trigger that state transition.

A complete admission path should answer:

1. **Authenticity:** was the request signed with the expected endpoint secret or verification key?
2. **Integrity:** were the method, destination, body, and security-relevant metadata covered?
3. **Freshness:** is the signed creation time inside an explicit acceptance window?
4. **Uniqueness:** has this delivery, event, or nonce already been processed?
5. **Context:** does the event belong to the expected tenant, account, environment, and object?
6. **State:** is this event legal from the object’s current workflow state?
7. **Effect:** can retries or reordering duplicate money, access, notifications, or provisioning?

[RFC 9421](https://www.rfc-editor.org/rfc/rfc9421.html) describes HTTP message signatures,
including covered components, creation and expiration times, and nonces that applications can use
as part of replay defenses. Provider-specific schemes vary. For example,
[Stripe’s webhook documentation](https://docs.stripe.com/webhooks) documents endpoint-specific
secrets, signed timestamps, recency checks, and the fact that retries are legitimate deliveries.
That means “reject every duplicate request” is too simplistic: the handler usually needs durable
event idempotency as well as cryptographic verification.

Do not replay a real production payment or subscription event merely to prove the point. Use the
provider’s test mode, a lab event, or a customer-provided fixture, and obtain explicit approval for
any test that changes business state. When even test-mode delivery is unavailable, review the
handler and configuration or limit the result to an unvalidated hypothesis.

## Custom domains, CDNs, and storage hide two owners in one URL

A hostname such as `help.customer.example` or `assets.customer.example` looks customer-owned to a
browser while the application behind it may be operated on a SaaS or delivery platform. DNS gives
the customer naming authority; provider configuration binds that name to a tenant, certificate,
origin, or storage namespace.

[Cloudflare’s custom-hostname documentation](https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/domain-support/create-custom-hostnames/)
illustrates this separation: a SaaS provider can onboard customer hostnames and select origin
behavior for them. The security review therefore needs to identify both the customer DNS decision
and the provider-side binding without treating the provider edge as a customer-owned network.

Useful customer-side questions include:

- Can a stale CNAME point at a deprovisioned or incorrectly bound tenant?
- Does ownership validation prevent another tenant from claiming the customer hostname?
- Is the expected hostname preserved through the CDN to the correct origin?
- Can a host-header or routing mismatch select another application or fallback origin?
- Do cache keys include the security-relevant host, authorization, locale, and content variants?
- Can an uploaded object change from private to public through metadata, transformation, or URL
  construction?
- Are signed delivery URLs bound to the intended object, action, audience, and lifetime?

Observe the approved hostname and its configuration. Do not expand a CNAME into the provider’s IP
ranges, fuzz global provider endpoints, attempt to claim resources, or interact with other tenants.
A dangling-binding hypothesis can often be established from customer DNS, provider documentation,
and a non-destructive error state. Actually claiming the resource may cause traffic capture and
requires exceptional written approval and a containment plan.

## Support, analytics, and telemetry are authorization boundaries too

Operational SaaS is easy to dismiss as “low impact,” but its trust is often different rather than
smaller.

A support platform may allow an agent to view account data, reset MFA, change an email address,
merge identities, trigger an administrative action, or impersonate a user. The relevant target is
the customer’s role model and the application action reached from the support workflow—not the
vendor’s unrelated tenants or employees. Test with named support roles and synthetic users. Record
who approved impersonation, how it is signalled, what is logged, and which high-risk actions require
step-up verification or a second person.

Analytics, chat, crash reporting, and observability platforms receive data from browsers,
applications, and backend services. Map what crosses the boundary: user identifiers, URLs, search
terms, headers, request bodies, stack traces, session replays, tokens, and tenant IDs. Then test the
customer’s redaction, sampling, environment separation, access roles, retention, and deletion
behavior. Do not search for real people or turn exposed employee information into a targeting
dataset.

This is also where architecture and supply-chain risk meet. NIST
[SP 800-161 Rev. 1](https://csrc.nist.gov/pubs/sp/800/161/r1/upd1/final) treats products and
services, how they are integrated, and the organisation’s visibility into those dependencies as
part of cybersecurity supply-chain risk management. A pentest cannot replace vendor due
diligence, contract review, incident planning, or continuous assurance. It can provide concrete
evidence about the customer-controlled integration paths most capable of transmitting risk.

## Convert scope into executable boundaries

“Third-party systems are out of scope” is too vague to guide an analyst. Replace it with an
integration-specific matrix.

| Plane or action                                   | Default state     | Example approved validation                                   |
| ------------------------------------------------- | ----------------- | ------------------------------------------------------------- |
| Public provider documentation and metadata        | Observe           | Record architecture and security-contract expectations        |
| Customer hostname and DNS                         | Decision required | Resolve and inspect the exact approved name                   |
| Customer callback or webhook endpoint             | Decision required | Send an engagement fixture within rate and state limits       |
| Customer test tenant configuration                | Decision required | Review or alter only named settings with rollback             |
| Customer application acceptance logic             | Decision required | Positive and negative test with synthetic accounts or events  |
| Provider global login, API, network, or admin UI  | Excluded          | No direct interaction beyond the approved normal journey      |
| Other provider tenants, users, employees, or data | Prohibited        | None                                                          |
| Production financial or destructive event         | Separate approval | Only with backup, rollback, monitoring, and emergency contact |

The Rules of Engagement should name the customer endpoint, tenant or account, test identities,
event fixtures, source addresses, time window, maximum rate, permitted state changes, excluded
provider assets, evidence handling, rollback owner, and emergency stop. If the engagement depends
on a provider feature that cannot distinguish test traffic from production traffic, that is a
constraint to escalate—not a reason to improvise.

<figure class="diagram">
<svg viewBox="0 0 700 350" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="A bounded third-party integration workflow moves from passive observation to a boundary hypothesis, written scope, controlled tests, evidence, and customer reporting, with stop paths for provider infrastructure and destructive effects.">
<defs>
<marker id="tptb-b" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto"><path d="M0,0 L8,4 L0,8 z" class="dg-arrow" /></marker>
</defs>
<rect x="20" y="34" width="128" height="58" rx="8" class="dg-box" />
<text x="36" y="58" class="dg-label">observe</text><text x="36" y="78" class="dg-muted">PASSIVE SIGNAL</text>
<line x1="150" y1="63" x2="180" y2="63" class="dg-line" marker-end="url(#tptb-b)" />
<rect x="184" y="34" width="142" height="58" rx="8" class="dg-box" />
<text x="200" y="58" class="dg-label">model boundary</text><text x="200" y="78" class="dg-muted">OBJECT · DECISION</text>
<line x1="328" y1="63" x2="358" y2="63" class="dg-line" marker-end="url(#tptb-b)" />
<rect x="362" y="34" width="142" height="58" rx="8" class="dg-box-accent" />
<text x="378" y="58" class="dg-label">written scope</text><text x="378" y="78" class="dg-accent">PLANE · LIMITS</text>
<line x1="506" y1="63" x2="536" y2="63" class="dg-line" marker-end="url(#tptb-b)" />
<rect x="540" y="34" width="140" height="58" rx="8" class="dg-box-accent" />
<text x="556" y="58" class="dg-label">controlled test</text><text x="556" y="78" class="dg-accent">ONE VARIABLE</text>
<line x1="610" y1="94" x2="610" y2="130" class="dg-line" marker-end="url(#tptb-b)" />
<rect x="540" y="134" width="140" height="58" rx="8" class="dg-box" />
<text x="556" y="158" class="dg-label">evidence</text><text x="556" y="178" class="dg-muted">REQUEST · EFFECT</text>
<line x1="538" y1="163" x2="508" y2="163" class="dg-line" marker-end="url(#tptb-b)" />
<rect x="362" y="134" width="142" height="58" rx="8" class="dg-box-accent" />
<text x="378" y="158" class="dg-label">customer handoff</text><text x="378" y="178" class="dg-accent">OWNER · FIX · RETEST</text>
<path d="M433 94 L433 246" class="dg-dashed" marker-end="url(#tptb-b)" />
<rect x="326" y="250" width="214" height="58" rx="8" class="dg-box" />
<text x="344" y="274" class="dg-crit">STOP · RE-SCOPE</text><text x="344" y="294" class="dg-muted">PROVIDER OR DESTRUCTIVE EFFECT</text>
<path d="M255 94 C254 176, 272 248, 324 277" class="dg-dashed" marker-end="url(#tptb-b)" />
<text x="20" y="336" class="dg-mono">NO ACTIVE STEP MAY OUTRUN THE WRITTEN PLANE</text>
</svg>
<figcaption>Discovery produces a hypothesis, not authorization. Provider interaction or a destructive effect returns the workflow to scope review instead of silently widening the test.</figcaption>
</figure>

## A fictional bounded assessment

Assume Meridian uses `login.meridian.example` for federated identity, receives billing events at
`api.meridian.example/billing/events`, and serves customer reports from
`files.meridian.example`. Public signals indicate that three different providers participate.

1. The analyst maps the redirects, CNAMEs, script references, and customer endpoints without
   authenticating or changing state. The providers enter the architecture graph as dependencies,
   all `excluded` for direct testing.
2. The customer confirms one isolated identity test tenant, one provider sandbox, two synthetic
   users, and a non-production storage namespace. The exact callbacks and hostnames are approved;
   provider administration outside those tenant settings remains excluded.
3. In the normal identity journey, the application creates the expected low-privilege account.
   A negative case changes the test tenant context while preserving a valid provider response. The
   application rejects it before local session creation. Evidence records the issuer, audience,
   tenant, callback, test identity, and lack of state change.
4. The sandbox emits a signed billing event. The handler validates it and changes a synthetic
   invoice once. A provider-generated retry returns success but does not duplicate the transition.
   An expired engagement fixture is rejected. No real payment event is replayed.
5. The approved file hostname resolves through the delivery provider. Tests confirm that a private
   object cannot be retrieved without the intended short-lived authorization and that changing the
   hostname does not select a fallback tenant or origin. The provider’s edge range is never scanned.
6. The report describes each customer control, the test fixture, expected and observed behavior,
   residual uncertainty, and the provider plane that remained untested.

Nothing in this assessment proves the providers themselves are secure. It proves that selected
customer-controlled trust decisions behaved correctly under the approved cases. That limitation
belongs in the result, not in a footnote.

## Evidence matrix

| Observation or test                                   | What it supports                                        | What it cannot prove                                      | Next decision                                             |
| ----------------------------------------------------- | ------------------------------------------------------- | --------------------------------------------------------- | --------------------------------------------------------- |
| CNAME, SDK, header, redirect, or privacy notice       | A provider relationship probably exists                 | Tenant ownership, configuration, vulnerability, or scope  | Corroborate the exact flow and ask who controls it        |
| Provider documentation describes a security mechanism | The available contract and expected control             | That the customer enabled or implemented it correctly     | Inspect customer configuration or acceptance behavior     |
| Approved normal identity journey succeeds             | The expected provider-to-application path is functional | Rejection of wrong issuer, audience, tenant, or claims    | Run one bounded negative case per relevant contract       |
| Invalid webhook signature is rejected                 | One authenticity failure is handled                     | Replay safety, idempotency, event context, or state order | Test time, duplicate, context, and workflow controls      |
| Signed retry causes no duplicate state change         | Handler idempotency for that synthetic event            | All event types or concurrent delivery behavior           | State the tested event and preserve concurrency limits    |
| Customer hostname reaches expected application        | Current name-to-tenant routing for that observation     | Exclusive binding or safe deprovisioning                  | Review ownership proof and lifecycle configuration        |
| Private object rejects an expired test URL            | One delivery authorization condition is enforced        | Upload policy, cache isolation, or cross-tenant behavior  | Map publish, transform, cache, and retrieve decisions     |
| Support role cannot perform a privileged test action  | That role boundary held for the named synthetic account | Provider-wide RBAC or every support workflow              | Expand only through customer-approved role and action map |

Evidence should retain the tenant, environment, account, endpoint, provider mode, request or
configuration reference, timestamp, expected effect, observed effect, cleanup, and remaining
uncertainty. Screenshots without these bindings are weak because they rarely show which plane was
actually tested.

## What the customer should receive

The deliverable should make the integration operable, not merely alarming.

**Dependency map.** Providers, customer hostnames, tenant identifiers, flows, data classes,
business owners, and observation times.

**Trust contract ledger.** For every identity, event, data, delivery, and support handoff: the
accepted object, verification rule, authorization rule, replay rule, state transition, and failure
behavior.

**Scope matrix.** The exact customer tenant, application endpoint, test account, fixture, and
action approved, beside the provider assets that remained excluded.

**Evidence packet.** A minimal positive and negative case, expected versus observed effect,
timestamps, cleanup, and reproducible retest instructions. Secrets and live customer data do not
belong in the packet.

**Ownership handoff.** Identity engineering owns claim and role mapping. Application teams own
callback and state-transition logic. Platform teams own DNS, edge, origin, cache, and storage
bindings. Support and privacy teams own operational access and data handling. Procurement and
risk teams own provider assurance that a pentest cannot establish.

The central reporting sentence should remain honest:

> We did not test the provider platform. We tested the named customer-controlled trust decision
> through the approved tenant, endpoint, fixture, and state transition.

That sentence is not a limitation of the methodology. It is evidence that the methodology knows
where authority ends.

The next article follows what happens after a passive platform reveals a live service:
**the service was observed, but the application—and the reason it matters—was still unknown.**