A secret committed to a public repository used to be a slow leak: someone had to find it, read the code around it and work out what it unlocked. Microsoft's latest report says an AI agent now does all three, and in one case it went from the search to stolen data in five minutes. This article covers what Microsoft observed, why deleting the commit does not help, and how we would remove the class of secret that leaks this way: the long-lived client secret your CI pipeline uses to reach the cloud.
What Microsoft saw
On November 5, Microsoft Threat Intelligence reported attackers using agentic tooling, naming the framework ARTEX, to hunt for secrets on GitHub. "Starting in August 2026, Microsoft observed threat actors using an agentic AI offensive framework to submit large volumes of queries to GitHub for secrets." The queries "often included the name of a target organization, keywords for secrets, a keyword linked to cloud services, or a keyword linked to another AI service provider."
The search was never the hard part. Microsoft's point is that the agent also handles what comes after it: it can "analyze the output and conduct follow-on attacks, cutting down on the need for the threat actor to have hands-on-keyboard interactions throughout the attack chain." The headline case: "In at least one case, Microsoft observed an AI agent advance from scanning GitHub for secrets to exfiltrating data from Azure Key Vault in just five minutes."
Two details matter for builders. First, Microsoft describes the agent recovering from its own mistakes, in sequences "consistent with adaptive troubleshooting". When an Azure call failed because it named the wrong API version, a rerun with a working version started "Approximately 28 seconds after the first failed response". Second, it goes after AI keys on purpose: "the AI agents repeatedly demonstrated a focus on credentials that provide access to AI services". Your model provider's API key is on the list, along with your cloud credentials.
Deleting the commit does not delete the key
The first instinct after a leak is to remove the file and force-push. Microsoft is direct about why that is not a fix: "Publicly exposed credentials remain usable until revoked or rotated; removing the original disclosure alone does not remediate the exposure." And again, in its mitigations: "Removing the content does not invalidate the credential or eliminate copies retained in edit history, caches, archives, logs, or other systems."
So a leak has one remedy, which is rotation, and it is a race. When the attacker's search and follow-up run at machine speed, we would plan as if that race is lost, and work toward a secret that cannot leak in the first place.
Give CI an identity instead of a secret
The secret most often committed by accident is the one a deploy needs: a cloud client ID and secret pasted into a workflow or a config file so the pipeline can sign in. Microsoft's own recommendation is to "migrate OAuth applications away from secret-based authentication", and notes that managed identities eliminate "the possibility of secret exposure in code".
For GitHub Actions, the replacement is OpenID Connect. GitHub's documentation puts it plainly: OIDC "allows your GitHub Actions workflows to access resources in Azure, without needing to store the Azure credentials as long-lived GitHub secrets." Each run asks GitHub for a short-lived signed token, and Azure trades it for an access token only if the token's claims match a federated credential you configured (this repository, this branch or environment). Here is a sketch of the deploy job, adapted from GitHub's Azure example:
name: deploy
on:
push:
branches: [main]
permissions:
id-token: write # lets the job request an OIDC token
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- uses: azure/login@v2
with:
client-id: ${{ vars.AZURE_CLIENT_ID }}
tenant-id: ${{ vars.AZURE_TENANT_ID }}
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
- run: az webapp deploy -g app-rg -n app --src-path dist.zip
What to notice: there is no client-secret anywhere. The three IDs are identifiers, not credentials, which is why they can live in plain repository variables. If this file leaked word for word, nobody could sign in with it from outside your repository, because Azure only issues a token to a run whose signed claims match your repository and environment. GitHub also notes that id-token: write "does not give the workflow permission to modify or write to any resources"; it only lets the job request the token. The environment: production line is what we would bind the federated credential to, so a pull request branch cannot deploy.
AWS and Google Cloud accept the same GitHub token through their own federation setup, so the pattern carries over if Azure is not your cloud.
Make the agent's speed work against it
Microsoft also describes defences aimed at the way agents behave rather than at a single IP address, which agents change quickly. One is decoys: "Fake wins present convincing evidence that the agent’s objective is complete, which can cause it to stop on a false success."
In practice, we would add a canary: a credential that looks real, has no permissions, sits somewhere a scanner would find it, and pages someone the moment it is used. Microsoft gives the signal to watch for on a real app too: sign-in errors "AADSTS7000215 (invalid secret) or AADSTS7000222 (expired secret) indicate someone holds a credential for your app; a failure followed by a success is urgent." An alert on those two codes for your service principals is one small query, and it turns the attacker's habit of retrying into your warning.
Where this costs you
Federation only covers services that accept it. Most AI model APIs still issue plain API keys, so OIDC does not remove your model key. For those, the rule is to keep the key on a server you run, never in a repository or a client, and to scope it so a stolen copy is cheap, as we covered in [Make a stolen AI key worth less](/insights/stolen-ai-api-keys-platform-breaches).
Moving a pipeline to OIDC takes setup on both sides: an app registration, a federated credential per branch or environment, and a role scoped to what the deploy touches. Get the subject condition wrong and either the deploy fails or the trust is wider than you meant. And it does nothing for secrets already leaked: those still need rotating today.
What to take from this
- Treat any secret that has touched a public repository as compromised, even after you delete it, and rotate it now.
- Replace the cloud client secret in your CI with OIDC federation bound to one repository and environment.
- Turn on secret scanning and push protection so supported secrets are blocked before they reach GitHub.
- Alert on failed sign-ins with invalid or expired secrets for your service principals, and plant one canary key.
If you want a second pair of eyes on how your pipelines and agents hold their credentials, book a conversation.
Sources
- Microsoft Threat Intelligence, "Threat actors use agentic AI tooling to find and abuse exposed secrets", November 5, 2026: https://www.microsoft.com/en-us/security/blog/2026/11/05/threat-actors-use-agentic-ai-tooling-to-find-and-abuse-exposed-secrets/
- GitHub Docs, "Configuring OpenID Connect in Azure": https://docs.github.com/en/actions/how-tos/secure-your-work/security-harden-deployments/oidc-in-azure



