Playbook ID: PB-SUPPLY | Default severity: SEV-2 (escalate to SEV-1 if the vendor holds standing credentials into a production system of record, or if access to regulated personal data is confirmed; drop to SEV-3 only once you have proven the integration held no live access) | Owner: Incident Commander
Open this playbook on any of:
tj-actions/changed-files (CVE-2025-30066) reached 23,000+ repositories; a backdoored aquasecurity/trivy-action stole LiteLLM's PyPI publishing tokens and malicious wheels shipped five days later (LiteLLM).Not for: takeover of your own tenant with no vendor involved (14.3), compromise of your identity provider (14.4), or exploitation of an edge appliance you operate (14.12) — use PB-SUPPLY to scope and cut standing access, then hand the appliance to 14.12. Vendor tiering, due diligence and contract clauses are Chapter 11. This is the day those stop being theoretical.
You did not get breached. You got included. Someone else's responders are having the worst week of their year, and the only thing you control is how much of your data is still reachable from inside their burning building. Third-party involvement now appears in roughly 48% of confirmed breaches — about a 60% year-over-year increase — and only 23% of third parties had fully remediated their known MFA issues (DBIR 2026 via SecurityWeek).
Your blast radius is defined by standing trust, not by the vendor's breach size. An OAuth refresh token is a key you cut for a contractor: it keeps working after you change your password, after the project ends, after they stop returning your calls — and, the part that ruins quarters, after someone lifts it out of their van. Salesloft Drift is the case to know. Attackers reached Salesloft's GitHub environment, pivoted into Drift's AWS environment, and stole the OAuth refresh tokens customers had issued to Drift. Between 8 and 17 August 2025 they exported records from 700+ organizations — Cloudflare, Google, PagerDuty, Palo Alto Networks, Proofpoint, Tanium and Zscaler among them — reaching Salesforce, Google Workspace and in some cases Slack (AppOmni; CSA). No customer had a vulnerability to patch. Every customer had work to do. And the highest-value loss was secondary: API keys, Snowflake tokens and passwords that customers' own staff had pasted into support-case text over the years. The CRM was the door; the ticket queue was the vault.
Teams get this wrong in two reliable ways. They wait — the vendor's disclosure timeline belongs to the vendor's counsel, while your GDPR clock runs from the moment you have reasonable certainty. And they perform containment theatre, rotating the vendor's password while the real exposure is a refresh token on infrastructure they do not control. Microsoft says it plainly: "Normal remediation steps (for example, resetting passwords or requiring multifactor authentication (MFA)) aren't effective against this type of attack, because these apps are external to the organization" (illicit consent grants). Actionable takeaway: treat every free-text field your vendors can read as a credential store that will eventually be exfiltrated, and secret-scan it on a schedule.
| Role | Responsibility in PB-SUPPLY |
|---|---|
| Incident Commander | Declares, sets severity, owns the containment-vs-availability call, runs the clock. No technical work. |
| Operations Lead | Integration inventory, revocation, rotation, hunt. Owns technical sequencing. |
| Vendor Liaison | Contract owner. Single channel to the vendor; issues the evidence demand; escalates commercially. |
| Communications Lead | Internal notice, customer holding statement, alignment with the vendor's public messaging. |
| Legal Liaison | Privilege, DPA and contractual clocks, controller/processor determination, teeth on the evidence demand. |
| Scribe | The four regulatory timestamps, decisions, approvals, and who touched which credential when. |
| Executive Sponsor | Approves service-affecting revocation, customer notification, vendor termination. |
Table conventions. `TIP-OFF marks a step the adversary can observe. EVIDENCE` marks a step that degrades evidence if run out of order. Do not reorder around those markers without the IC.
| # | Action | Who | Done when | Evidence to capture |
|---|---|---|---|---|
| 1.1 | Declare; open the log. Record four timestamps: first awareness, reasonable belief an incident occurred, determination data was affected, materiality determination. Different clocks run from different ones. | IC / Scribe | Four fields present (three may be blank) | Declaration; triggering report verbatim |
| 1.2 | Identify the vendor precisely: legal entity, product, your tenant ID, contract, DPA, subprocessor list, named security contact. | Vendor Liaison | Vendor record pulled from the Ch. 11 inventory | Contract, DPA, subprocessor annex, notification clause |
| 1.3 | Build the integration inventory — every path, not the obvious one: OAuth grants and service principals; keys you issued them and keys they issued you; SSO/SAML and SCIM accounts; webhooks and signing secrets; SFTP drops; VPN peers and allowlisted IPs; shared vault secrets; human vendor logins. | Ops Lead | One list, an owner per row, no row marked "unknown" | The list, timestamped — it is the incident's scope document |
| 1.4 | Enumerate consent grants tenant-wide, filter to the vendor (script below; Workspace OAuth Token log events; connected-app list in each SaaS system of record). | Ops Lead | Grants exported with scopes | Permissions.csv; flag ConsentType = AllPrincipals |
| 1.5 | Export logs before touching anything. Entra sign-in/audit, Purview unified audit, Workspace admin/OAuth/Drive, CloudTrail, SaaS event logs. Retention is short; holds are not retroactive. `EVIDENCE` if skipped | Ops Lead | Exports cover the vendor's window plus 30 days either side | Manifests with hashes; the queries; each source's retention |
| 1.6 | Place legal holds (M365 eDiscovery hold, S3 Object Lock legal hold, equivalents) on mailboxes, sites and buckets the vendor could reach — before containment. | Legal Liaison | Hold confirmed in tooling | Hold ID, scope, custodians, applier |
| 1.7 | Hunt the vendor's identity across your estate for the window: every action by their application ID, service principal, integration user and source ranges. Look for reads outside the normal object set, volume spikes, odd hours (T1078 Valid Accounts). | Ops Lead | Query run against every system in 1.3 | Query text, results, record counts accessed |
| 1.8 | Secret-scan free text the vendor could read — support cases, ticket comments, CRM notes, chat exports, attachments — for keys, tokens, connection strings, passwords. | Ops Lead | Scan complete; hits triaged into a rotation queue | Redacted scan output; rotation queue with an owner per secret |
| 1.9 | Classify against the six notification axes (personal data / regulated service / your product / materiality / extortion / AI system) and set severity. An incident can sit on several at once. | IC / Legal | Severity set; notification owner named, distinct from the IC | Classification worksheet with reasoning, not just the answer |
# Entra ID — enumerate every delegated consent grant in the tenant.
# Microsoft's documented method; run under Microsoft Graph PowerShell.
.\Get-AzureADPSPermissions.ps1 | Export-Csv -Path "Permissions.csv" -NoTypeInformation
# ConsentType = AllPrincipals means the app can reach EVERY user's content.// Defender XDR — everything one OAuth application did. Populated ONLY if
// Defender for Cloud Apps and the Microsoft 365 activities connector are on;
// otherwise this returns nothing, silently.
CloudAppEvents
| where OAuthAppId == "<application id>"
| project ActionType, AccountObjectId, IPAddress, UserAgent, IsAdminOperation, UncommonForUserThe usual advice — posture quietly, then remediate in one burst so you do not tip off the adversary — partially inverts here. In your own estate you can watch an intruder while you build the picture. In your vendor's estate you have no telemetry, no authority and no ability to observe, and their containment and disclosure will tip the actor off regardless. So: scope fast, time-boxed, then contain your side in one atomic burst. Splitting revocation across days hands the adversary the paths you have not closed yet.
| # | Action | Who | Done when | Evidence to capture |
|---|---|---|---|---|
| 2.1 | Confirm 1.5 and 1.6 are complete. Everything below this line degrades live telemetry. | IC | Manifests and hold IDs attached | Sign-off naming who confirmed |
| 2.2 | Revoke the OAuth grant, not the password. Entra: Remove-MgOauth2PermissionGrant (delegated) and Remove-MgServicePrincipalAppRoleAssignment (application permissions). Workspace: tokens.delete per user. `TIP-OFF` | Ops Lead | Grant absent on re-enumeration | Before/after grant export; call, result, operator, UTC time |
| 2.3 | Revoke sign-in sessions for every account the integration touched, including the integration accounts. Session revocation and credential reset happen in the same action, never sequentially. `TIP-OFF` | Ops Lead | Revocation succeeds for all in-scope principals | Command output per principal; the account list |
| 2.4 | Rotate every secret from 1.3 and the 1.8 queue: keys in either direction, webhook signing secrets, SFTP credentials, shared service accounts. Deactivate before deleting, so you can still prove what was used. | Ops Lead | Old credential inactive and confirmed unused | Last-used output before deactivation; rotation record per secret |
| 2.5 | AWS cross-account: revoke role sessions and change permissions — revocation alone is not containment. Prefer a quarantine SCP from the management account; an account admin cannot detach an SCP. | Ops Lead | Sessions denied; SCP or AWSDenyAll applied; new calls fail | Policy JSON with timestamp; attach-policy output; CloudTrail denials |
| 2.6 | Network paths: remove vendor IPs from allowlists, disable the VPN peer or ZTNA segment, disable vendor jump-host accounts. Changing a security group does not terminate established connections — use NACLs for live sessions. | Ops Lead | Path closed and verified by test | Change record with rule IDs; before/after connectivity test |
| 2.7 | SSO and provisioning: remove the vendor app's user assignments in your IdP; disable the SCIM account. Read the limits callout before declaring containment. `TIP-OFF` | Ops Lead | Assignments removed; new sign-ins fail | IdP audit entries; test sign-in showing denial |
| 2.8 | Build-chain variant: pin the last known-good version by digest, purge the poisoned artefact from registries and caches, invalidate every CI publishing token and registry credential, and rotate every secret the runner could read — runners hold more standing privilege than any human user. | Ops Lead | Clean build reproduced from pinned digests | Lockfile diff; purge log; list of rotated runner secrets |
| 2.9 | Build-chain variant: hunt attacker-created persistence in source control — new repositories, workflows, deploy keys, maintainer accounts. Shai-Hulud exfiltrated via attacker-created repos and workflows and republished itself under compromised maintainer accounts (CISA). | Ops Lead | Org-wide enumeration complete | Repo/workflow creation events with actor and timestamp |
| 2.10 | Issue the evidence demand in writing through the single channel — in parallel with containment, never instead of it. | Vendor Liaison / Legal | Sent, acknowledged, response deadline set | The demand; acknowledgement; response log |
# Entra — revoke sessions for an in-scope account. Resets
# signInSessionsValidFromDateTime, killing refresh tokens and session cookies.
# Privileged Authentication Administrator for admin accounts; User Administrator otherwise.
Connect-MgGraph -Scopes "User.ReadWrite.All","Directory.AccessAsUser.All"
$User = Get-MgUser -Search UserPrincipalName:'svc-vendor@contoso.com' -ConsistencyLevel eventual
Revoke-MgUserSignInSession -UserId $User.Id# Google Workspace — BOTH calls are required; they do different jobs.
# Scope: https://www.googleapis.com/auth/admin.directory.user.security
# 1. Sign out everywhere and reset sign-in cookies.
POST https://admin.googleapis.com/admin/directory/v1/users/{userKey}/signOut
# 2. Revoke the third-party OAuth grant. signOut does NOT do this — without
# this call, the vendor's app keeps working.
DELETE https://admin.googleapis.com/admin/directory/v1/users/{userKey}/tokens/{clientId}// AWS — the policy the console attaches as AWSRevokeOlderSessions. Denies
// sessions assumed before the timestamp (plus ~30s of propagation slack).
// Requires PutRolePolicy. Service-linked roles cannot be revoked this way, and
// roles from IAM Identity Center permission sets must be revoked in Identity Center.
{
"Version": "2012-10-17",
"Statement": {
"Effect": "Deny", "Action": "*", "Resource": "*",
"Condition": { "DateLessThan": {"aws:TokenIssueTime": "2026-09-05T14:20:00Z"} }
}
}| # | Action | Who | Done when | Evidence to capture |
|---|---|---|---|---|
| 3.1 | Enumerate and remove persistence created through the integration: new app registrations and service principals, new API keys, new federated identities, added users, elevated roles, mailbox rules and forwarding (T1098 Account Manipulation). | Ops Lead | Every object created by the vendor's principal dispositioned | Object list with creating actor and timestamp |
| 3.2 | Scope what was actually read. In Exchange Online use MailItemsAccessed, pivoting on SessionID, ClientInfoString and AppId; check IsThrottled — over 1,000 records in 24h stops logging for that mailbox, and its presence is itself a signal. Elsewhere, pull the object-level event log for the application. | Ops Lead | Record- or folder-level scope determined per data store | Query output; counts by data category; explicit list of what could not be scoped and why |
| 3.3 | Clear the 1.8 rotation queue. Every credential found in ticket text is compromised whether or not you can prove it was read. | Ops Lead | Queue empty; each secret rotated and re-owned | Rotation record; confirmation the old value is inactive |
| 3.4 | Rotate downstream of those secrets — a Snowflake token or cloud key in a ticket may grant further access. Walk the chain until it stops. | Ops Lead | Downstream systems enumerated and dispositioned | The chain walked, with a decision per node |
| 3.5 | Re-run every Phase 1 enumeration and diff against the pre-containment export. New grants, keys or accounts after containment mean a path is still open. | Ops Lead | Diff clean, or findings raised | The diff; both exports retained |
| 3.6 | Loop-back rule: any new indicator from 3.1–3.5 stops eradication. Return to Phase 1 and re-scope. Do not recover on a scope you just invalidated. | IC | IC records "no new indicators" or a re-scope decision | The explicit statement in the log |
| 3.7 | Chase the evidence demand; log every non-answer with its date. A pattern of non-response is a finding for the review and the renewal. | Vendor Liaison | Response received, or escalated to the Executive Sponsor | Correspondence thread; gap list |
What to demand from the vendor — and what you will probably get. Ask in writing for: the exposure window with start and end times; whether your tenant identifier appears in their access logs; the objects, fields and record counts reached in your instance; whether credentials you issued them were in scope; the log sources searched and their retention; indicators you can hunt with; whether law enforcement or a regulator is involved; and their written position on controller versus processor. What comes back is usually a status-page update and a confidentiality request. Send it anyway, keep the thread, and put the gaps in the review. Actionable takeaway: the time to negotiate evidence access is at contract signature — so send Chapter 11's clause list to procurement the week after this closes, while the pain is still fresh enough to win the argument.
| # | Action | Who | Done when | Evidence to capture |
|---|---|---|---|---|
| 4.1 | Decide whether to reinstate at all. A business decision with a security input; it needs a named owner and a date, not a quiet drift back to the old state. | Exec Sponsor / IC | Documented: reinstate, reinstate reduced, replace, or terminate | Decision, reasoning, reviewer, review date |
| 4.2 | If reinstating, re-issue at least scope: narrowest permission set that works, a dedicated service identity (never a human's account), fresh credentials, and short-lived credentials or workload federation instead of long-lived keys where supported. | Ops Lead | New grant live with scopes recorded | Old vs. new scope diff; approval; who granted it |
| 4.3 | Set an expiry date and a named owner who must re-approve. A grant with no expiry becomes standing trust again within a quarter. | Ops Lead | Expiry in the vendor inventory with a calendar owner | Inventory record showing expiry and owner |
| 4.4 | Add detection for this vendor: the integration principal reading outside its normal object set, volume above a measured baseline, new source ranges, any new consent grant naming the vendor. | Ops Lead | Rule deployed and validated against a replayed true-positive sample | Rule definition; validation evidence; alert routing |
| 4.5 | Verify containment held by observation over a defined watch period (14 days recommended): no new tokens issued to the app, no sign-ins from old ranges, no calls from rotated credentials. | Ops Lead | Watch period complete with a written result | Watch queries and results at start, midpoint, end |
| 4.6 | Close against explicit exit criteria: containment verified, business function confirmed at reduced scope, scope of access determined or formally recorded as undeterminable, notifications filed, evidence under hold. | IC | All criteria met and signed | Closure record naming who verified each |
| # | Action | Who | Done when | Evidence to capture |
|---|---|---|---|---|
| 5.1 | Blameless review within 10 business days. The subject is your detection and revocation speed, not the vendor's failings — you controlled one of those. | IC | Review held; actions owned and dated | Review record; action register |
| 5.2 | Measure two numbers: vendor disclosure → full revocation, and vendor disclosure → completed exposure scope. These are what the board sees next quarter. | IC / Scribe | Both calculated from the incident log | The calculation with its source timestamps |
| 5.3 | File the remaining regulatory reports on their own clocks — NIS2 final report at one month, DORA final report one month after the intermediate, plus supplementals. Chapter 15 has the detail. | Legal Liaison | All filings submitted and acknowledged | Filing receipts; reporting register |
| 5.4 | Update the vendor inventory and tier (Chapter 11) on evidence: scopes actually held, data actually reachable, response quality actually observed — not the questionnaire they filled in two years ago. | Vendor Liaison | Inventory updated; tier changed or re-affirmed with reasoning | Inventory diff |
| 5.5 | Send the contract gap list to procurement: evidence access rights, per-tenant log provision, notification clock, subprocessor notice, audit rights, termination for security cause. | Legal Liaison | Delivered with a named owner in procurement | Gap list; acceptance |
| 5.6 | Institutionalize the secret-hygiene fix: secret-scanning on support-portal submissions and a standing scan of ticket bodies and CRM notes. Then convert this response into a tabletop inject for the next exercise cycle (Chapter 18). | Ops Lead / IC | Control live and producing findings; inject scheduled | Configuration; first findings report; the inject |
Two things start clocks here, and only one of them is the vendor's announcement.
Two determinations must happen early and in writing. First, controller or processor: if you are the controller and the vendor is your processor, the duty to the supervisory authority and to data subjects is yours, and pointing at the vendor is not a defense. Second, your downstream duty: if your customers' data sat in that platform, you owe your customers notice on your contract's clock regardless of what the vendor tells the world. Chapter 15 carries the full matrix, templates and decision tree — never let a technical responder file a regulatory early warning without disclosure-counsel review of the wording. And agree what you will say publicly before the vendor's next update lands. Contradicting your vendor in public is a second incident, and it is the one the press will cover.
The highest-value automation here is not a containment action. It is a "vendor name in, integration inventory out" workflow that answers step 1.3 in under five minutes: every OAuth grant, service principal, API key, SSO assignment, SCIM account, webhook and allowlist entry tied to a named vendor, pulled live from your IdP, cloud accounts and SaaS admin APIs. Most teams take a day and a half to assemble that by hand, and the whole incident queues behind it. Build that one thing and you buy back a day of exposure on every future vendor breach.
Safe to automate — reversible, scoped, verifiable after the fact: grant and key enumeration; diffing today's grants against a stored baseline; exporting logs and applying legal holds; pulling all activity for a named application ID; secret-scanning support tickets; opening the ticket and paging the roles; running the Phase 3 re-enumeration diff on a schedule.
Human gate required — irreversible, or blast radius that scales with a false positive: revoking a grant carrying production traffic; disabling SSO federation or SCIM; deleting an OIDC identity provider (there is no disable operation, only delete, and every role that trusts it stops working); attaching a quarantine SCP; rotating a shared secret with no tested rollback; and every customer or regulator notification. Microsoft recommends against the tenant-wide blunt instrument of disabling integrated applications — a script that "revokes all third-party grants" will take your business offline faster than the adversary would have.
The gate rule that holds up: automation may gather, enrich, correlate and recommend without approval; it may act only where the action is reversible, scoped and rate-limited; irreversible or tenant-wide actions require a named human approver. Every automated action writes its evidence into the incident log — an action with no artefact is one you cannot prove to a regulator six months from now.