Search every repo for three things: dependency names of AI client libraries, the hostnames of AI APIs, and calls that load or download models. Then check configuration and environment files for the keys those calls use. Together they give you a first inventory of AI usage that nobody wrote down.
Why code search works for shadow AI
A service that calls a model API has to say so somewhere. It needs a library or an HTTP client, a hostname, and a credential. Those three leave traces in manifests, source files and config. That is good news, because repo search is cheap and your source host probably supports it already.
GitHub's code search, for example, supports an org qualifier, path filters, boolean operators and regular expressions wrapped in slashes. Git's own grep works on a local clone and can search a given revision. Either is enough for a first pass.
Build your search list first
You need a list of what to look for, and it is specific to the providers you care about. Do not guess it from memory. For each AI provider or framework your company might use, write down from its official documentation:
- The package names for its client libraries, in each language you use.
- The API hostnames.
- The environment variable names its docs tell you to set.
Keep that list in a file in a repo so people can add to it. It will grow.
The four searches
- Dependencies. Search manifests and lockfiles for each package name on your list. This finds declared use.
- Hostnames. Search all source, config and infrastructure files for each API hostname. This finds use through plain HTTP calls with no library.
- Credentials. Search for the environment variable names, and for key-like strings in config. A hit in a committed file is both an inventory item and a leak to fix. See inventory AI provider API keys.
- Model loading. Search for calls that download or load model files from a hub or a path. Pair this with a file search for model formats: see how to find model files in repos and artifact stores.
Example, using git grep on a clone:
git grep -nIiE 'api_key|embeddings|completions' -- '*.py' '*.ts' '*.js' '*.yaml' '*.env*'
git grep -lE 'YOUR_HOSTNAME_ONE|YOUR_HOSTNAME_TWO'
The first line is deliberately broad and will be noisy, so treat it as a lead generator, not a verdict.
Record what you find
For each hit, capture the repo, the service called, who owns the repo, whether it is production code, and what data goes in. A table works:
| Repo | AI service | Owner | Production | Data sent |
|---|---|---|---|---|
| payments-api | (fill in) | (team) | Yes | (describe) |
The last column is the one that matters for risk. If the owner cannot describe the data, that is your next conversation.
What this will not find
Code search sees what is committed. It will not see a developer pasting code into a chat window, a notebook running on a laptop, or a call made from a personal account. For those, you need policy, training and an approved path that is easier than the workaround. See AI tool use training for employees.
OWASP's guidance on LLM supply chain risk also treats models, datasets and adapters as part of the supply chain, which is why inventory has to go beyond the libraries.
Quick check
Should we block AI use once we find it?
Not automatically. Find the owner, check the data involved, and decide whether to approve it, replace it or remove it. A blanket block tends to push the use out of sight.
How often should we repeat the search?
Run it on a schedule or on every pull request. Usage appears whenever a developer has a new idea, so a one-time search ages fast.
Safeguard offers asset discovery, SBOM and dependency scanning and a free scan at gold.safeguard.sh/scan to help build the inventory this search starts.
Sources
- GitHub Docs: Understanding GitHub code search syntax
- Git documentation: git-grep
- OWASP GenAI: LLM03 Supply Chain
Last reviewed: 19 October 2026.