chevron-left chevron-right

Piotr Nalepa Frontend, architektura i AI

Shadow AI in a development team: how to keep your client’s code out of the chat?

Ta wersja
blog.piotrnalepa.pl/en/2026/10/27/shadow-ai-in-a-development-team-how-to-keep-your-clients-code-out-of-the-chat
Wersja polska
Shadow AI w zespole programistycznym: jak nie wynieść kodu klienta do czatu?
Opublikowano
Autor
, Staff Software Engineer
Kategoria
AI
Uwagi
Napisz do mnie · Napisz na X

It takes three seconds to paste a production stack trace into an AI chat. It takes much longer to explain to your client why their customers' email addresses ended up on a third-party server. That gap is what shadow AI is about, and development teams working for external clients are right in the middle of it.

Don't get me wrong, this is not an article against AI. AI assistants are part of everyday development work now, and I don't think there is a way back. The problem is not that developers use AI. The problem is that very often nobody knows which tools the team uses, on which accounts, and what goes into them.

In this article I'm going to explain what shadow AI is, how it shows up in a development team, and why it gets more serious when the code belongs to a client. Then we'll go through practical ways to deal with it: from giving the model less data, through pseudonymizing what you have to send, to configuring your IDE assistants so they don't read what they shouldn't.

This article is also available in Polish: Shadow AI w zespole programistycznym: jak nie wynieść kodu klienta do czatu?.

Sekcja 1What is shadow AI?

Shadow AI is any use of AI tools at work that happens without the knowledge or approval of the company, its managers or its IT department. It's a younger sibling of shadow IT, the unapproved apps and services people have always installed to get their work done.

Palo Alto Networks points out what makes it different. Shadow IT is mostly about unsanctioned access and infrastructure. With shadow AI you also have to worry about what the model does with your data, what it generates, and which decisions get made based on its answers.

Two things count as shadow AI even though they don't look like it at first glance:

  • AI features that appear in tools you have already approved. Your issue tracker, your team chat or your video calls can get an AI assistant with the next update, and nobody reviews it,
  • Personal accounts in tools you have approved. The same chat app can be a sanctioned company tool or shadow AI. What makes the difference is the account and the contract behind it, not the logo.

How common is it? A few numbers:

Source What it found
Palo Alto Networks, "State of GenAI 2025" GenAI traffic grew by more than 890% in 2024, and an average organization had 66 GenAI apps, 10% of them classified as high risk
Palo Alto Networks, as above GenAI-related data loss incidents grew more than 2.5 times and now make up 14% of all such incidents
CybSafe, 2024, 7 countries 38% of employees shared sensitive work data with AI tools without their employer knowing
ESET and DAGMA, Poland, 2026 62% of employees use AI at work, but only 27% of companies have a written AI policy

And the story everyone in our industry remembers: in 2023 an engineer at Samsung pasted confidential source code into ChatGPT to optimize it. Samsung's answer was to ban generative AI tools for its employees. As you will see in a moment, a ban is the answer that usually doesn't work.

Sekcja 2Why do developers go around the rules?

Mostly because the tools are genuinely useful. A model explains a cryptic stack trace, writes the test nobody wanted to write, or spots why a query is slow, all in a few seconds. When the sprint ends on Friday, that's hard to ignore.

The Polish Agency for Enterprise Development (PARP) names two more reasons, and IMO they matter even more:

  • There are no rules. If nobody said which tools are fine, people use the ones they know,
  • The rules are too slow. If approving a new tool takes weeks, a developer with a deadline will not wait for it.

I would add a third one: the approved tool is worse. If the company license is for an assistant that is clearly behind what a developer uses at home, the developer will quietly switch to the one at home.

So why not just ban it? Because a ban removes the visibility, not the need. In the Polish ESET and DAGMA study, 35% of AI users said they would try to get around company blocks, and 26% would generate what they need on a private device and send it to their work email. Palo Alto Networks lists "banning AI stops shadow AI" as one of the most common myths.

Sekcja 3What does it look like in a development team?

Nobody wakes up thinking "today I'm going to leak our client's data". It's always a small shortcut that makes sense at that moment.

Data going out:

  • Logs and stack traces pasted to debug faster, with email addresses, user IDs, IP addresses and sometimes a bearer token from a request header,
  • Database rows pasted so the model can write a query or a migration,
  • Client code pasted into a personal account, because the company only pays for a different tool,
  • Specs and emails from the client summarized or translated, with names, release dates and partner names inside,
  • Meeting bots that record a client call and send the audio to an external service, often without asking the other side.

Tools reaching in:

  • An IDE agent that reads the whole repository, including .env, because nobody told it not to,
  • AI browser extensions that can read every page you open, including the client's issue tracker and wiki,
  • MCP servers connected to the client's Jira, Slack or database with a personal token that can do far more than the task needs.

The second group is the one I would watch most closely. Agents that read files, run commands and talk to other systems on their own are no longer an experiment. Palo Alto Networks describes overprivileged access, leaked credentials and unreviewed tool calls through MCP as a new class of risk. One of their examples is a developer who built an internal chatbot on customer data through an external model API, and the project never made it to the security team's backlog.

There is also traffic in the other direction. Generated code is often reviewed less carefully than code written by a colleague, even though it deserves exactly the same scrutiny. Some time ago I wrote about what a good code review should check (in Polish), and none of it stops applying just because a model wrote the code. On top of that, models sometimes recommend packages that don't exist. Attackers know that: they register those made-up names on npm and wait for someone to run npm install.

Sekcja 4Why is it worse when you work for a client?

If you build your own product, leaked code is your own problem. If you build for a client, the code, the data and the documents are theirs, and you only work on them under an agreement. That changes the picture.

The contract sets the rules. NDAs, copyright transfer for the code, and more and more often an explicit clause about AI tools. If the agreement says you can't share the code with third parties, an AI provider is a third party, no matter how good its privacy page looks.

Personal data is borrowed, not yours. If the client's systems hold their users' data, the client shared it with you under specific conditions. In the EU this is covered by GDPR, the European data protection regulation. Sending the data to an AI provider means adding one more processor to the chain, and the client has to agree to that first. It may also mean that European data lands on servers outside the EU, without any record of where it went. GDPR fines go up to 20 million euro or 4% of annual turnover. Outside the EU the rules have different names, Palo Alto Networks mentions HIPAA for health data in the US or the DPDP Act in India, but the idea is the same: the data belongs to someone else, and they decide who gets it.

You can't answer the simplest question. When the client asks "where did our data go?", you need an answer. With shadow AI there are no logs, no list of tools and no agreement with the provider. Palo Alto Networks calls it a lack of auditability. Clients call it a reason to look for another vendor.

Sekcja 5How to deal with shadow AI?

Every source I went through for this article ends up in the same place: bans don't work, but clear rules, education and approved alternatives do. PARP specifically warns that complicated approval procedures push people to act on their own. My short version: make the safe way the easy way. Here is how.

Sekcja 5.1Give the team tools worth using

If the company tool is worse than the personal one, the personal one wins. According to ESET and DAGMA only 38% of Polish companies pay for corporate AI licenses, which says a lot about where the rest of the usage comes from.

  • Pay for business plans. Consumer plans of many AI services may use your conversations for training unless you opt out. Business plans and API access usually rule that out in the contract. These terms change often, so check them for the provider you actually use,
  • Route model traffic through one gateway. A shared proxy, for example LiteLLM, instead of everyone's personal API keys. You get one place for keys, budgets, logs and data filters, and some gateways can mask personal data before a request leaves your network,
  • Write down which tool is approved for what. A short section in the README or on the wiki is enough. If writing things down is not a habit in your team yet, I described how we built one in Creating connected remote teams.

Sekcja 5.2Decide what may go where

Palo Alto Networks recommends starting with the data that must never go into GenAI tools. In a development team, a small table works better than a long policy:

What Personal chat account Company AI tool with a contract Local model
Public docs, open source code Yes Yes Yes
Your own product's code No Yes Yes
Client code No Only with the client's OK If the agreement allows it
Production personal data No Anonymized, with the client's OK Anonymized
API keys, passwords, .env Never Never Never

Notice that the last row is the same everywhere. A secret that has been sent to any model is a leaked secret. Rotate it.

Sekcja 5.3Send less

The best way to protect data is not to send it. And most of the time the model doesn't need it anyway:

  • A query needs the schema, not the rows. Paste the CREATE TABLE statement or the output of \d orders instead of SELECT * ... LIMIT 100,
  • A bug needs a minimal reproduction, not the whole module. Cut it down to the few lines that fail. Half of the time you will spot the cause while doing it,
  • Tests need realistic data, not real data. Generate it.
Przykład 1TypeScript
import { faker } from '@faker-js/faker'

const users = Array.from({ length: 50 }, () => ({
  id: faker.string.uuid(),
  name: faker.person.fullName(),
  email: faker.internet.email(),
  city: faker.location.city(),
}))

And make sure your logs are safe to share before anyone needs to share them. If you use pino in Node.js, the redact option does it for you:

Przykład 2TypeScript
import pino from 'pino'

const logger = pino({
  redact: [
    'req.headers.authorization',
    'req.headers.cookie',
    'user.email',
    'user.phone',
  ],
})

Those fields show up in the log as [Redacted], so pasting a stack trace becomes much less risky. Your on-call colleagues and the client's support team will be grateful too. Keep in mind that redact works on field names. An email inside an error message stays in the log.

Sekcja 5.4Pseudonymize what you have to send

Sometimes the real content matters, for example when you want the model to analyze a bug report from the client or summarize a requirements document. In that case, strip out what identifies people and companies before you send it.

There are two ways to do it, and if you work with European data, the difference really matters:

  • Anonymization is one-way. Once done, nobody can tell whom the data was about, so GDPR no longer applies to it,
  • Pseudonymization replaces the data with placeholders and keeps a mapping on your side. You can put the originals back into the model's response, but as long as the mapping exists, GDPR still treats it as personal data.

For working with a model, pseudonymization is usually the practical choice. The client's name becomes [NAME_1], an email becomes [EMAIL_2], an IP address becomes [IPV4_1]. The model still sees the shape of the problem, just not who it belongs to. A simplified version:

Przykład 3TypeScript
const PATTERNS: Array<[string, RegExp]> = [
  ['EMAIL', /[\w.+-]+@[\w-]+\.[\w.-]+/g],
  ['IPV4', /\b(?:\d{1,3}\.){3}\d{1,3}\b/g],
  ['IBAN', /\b[A-Z]{2}\d{2}[A-Z0-9]{11,30}\b/g],
]

export function pseudonymize(text: string, names: string[]) {
  const tokens = new Map<string, string>() // original -> placeholder
  const counters = new Map<string, number>()

  const tokenFor = (label: string) => (value: string) => {
    if (!tokens.has(value)) {
      const next = (counters.get(label) ?? 0) + 1
      counters.set(label, next)
      tokens.set(value, `[${label}_${next}]`)
    }

    return tokens.get(value)!
  }

  let result = text

  for (const [label, pattern] of PATTERNS) {
    result = result.replace(pattern, tokenFor(label))
  }

  for (const name of names) {
    result = result.replaceAll(name, tokenFor('NAME'))
  }

  return { text: result, tokens }
}

export function restore(text: string, tokens: Map<string, string>) {
  let result = text

  for (const [original, token] of tokens) {
    result = result.replaceAll(token, original)
  }

  return result
}

The names list covers what no regular expression can guess: the client's name, project code names, people on the client's side, internal hostnames. Keep it in the project repository, so the whole team works with the same list.

Of course, a handful of regular expressions won't catch everything. For anything serious, take a look at Microsoft Presidio. It combines patterns with NLP models to detect personal data, and you can add your own recognizers for identifiers specific to your client or country. Even better, run it inside the gateway from the first section, so it happens on every request and not only when someone remembers.

Two things to keep in mind:

  • Models don't always respect placeholders. Sometimes [NAME_1] comes back as "Name 1" or without the brackets, and restore won't find it. Read the answer before you forward it,
  • Code is much harder to anonymize than text. Package names, class names, service URLs, comments and the business logic itself give the client away. Pseudonymization works well for logs and documents. For client code the right answer is an approved tool and the client's consent.

Sekcja 5.5Fence off secrets in your IDE

An assistant in your editor sees a lot more than a chat window in the browser. An agent exploring the repository will happily open .env if nothing stops it. Most tools let you exclude files:

  • Claude Code: deny rules in .claude/settings.json,
  • Cursor: a .cursorignore file, with the same syntax as .gitignore,
  • GitHub Copilot: content exclusion in the repository or organization settings, on the Business and Enterprise plans.

For Claude Code it looks like this:

Przykład 4JSON
{
  "permissions": {
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(./secrets/**)"
    ]
  }
}

Commit it, and the rule protects everyone on the team, not just the person who thought about it.

Still, treat it as a guard rail, not a vault. The safest secret is the one that never lands on a developer's machine. Use development keys locally, keep production credentials in a secrets manager, and run a secret scanner such as gitleaks in a pre-commit hook to catch what slips through.

Sekcja 5.6Run a model locally when nothing may leave

Some clients, in banking, healthcare or the public sector for example, won't accept any external service at all. For them, a model running locally through Ollama or LM Studio can be the answer. The data never leaves the machine, so most of this article simply doesn't apply.

The trade-off is quality and hardware. Local models are behind the best cloud models and need plenty of memory. For explaining code, writing tests or reviewing a diff they are often good enough, though. Another option is a model hosted by a cloud provider the client already works with, in the client's own region, which for European clients usually means the EU.

Sekcja 5.7Talk about AI at the kick-off

The cheapest time to agree on AI is before the project starts, not after an incident. I would add a short AI section to every kick-off:

  1. Are we allowed to use AI assistants on your code at all?
  2. Which tools and which plans? Do you have an approved list, or a license we should use?
  3. May personal data from your systems reach an AI provider? If yes, which one? For EU clients, add it to the data processing agreement you have under GDPR.
  4. May we record and transcribe our meetings?
  5. Should AI-generated code be marked, or go through an extra review?

Put the answers in the contract, or at least in the project docs, next to the rest of your working agreements. And if the client has their own AI license, use it for their code. Then they decide where their data goes, and you have it in writing.

Sekcja 5.8Know what's running, and make asking easy

Palo Alto Networks starts with visibility, and it makes sense: you can't manage what you can't see. For a development team, that means knowing about:

  • IDE and browser extensions,
  • MCP servers and the tokens agents use,
  • meeting bots connected to calendars,
  • AI features that appeared in Jira, Slack or GitHub after an update.

Review MCP configuration, such as a .mcp.json file in the repository, in pull requests, the same way you review a new dependency in package.json. Give agents their own tokens with the narrowest scope possible, one per client, and read-only whenever you can.

Then make asking easy. A short form, one person who decides, and an answer within days, not months. Palo Alto Networks also suggests reviewing approved tools every 6 to 12 months, since new AI features tend to arrive without an announcement.

Sekcja 5.9Teach with real examples

Nobody reads a ten-page AI policy. What sticks is an example from your own project: a stack trace full of customer emails, or an .env file sitting in an agent's context. Palo Alto Networks recommends the same, education built on real-world cases rather than rules alone.

If you work in the EU, it's no longer just a good habit, by the way. Since February 2025 the EU AI Act expects companies that use AI to make sure their people know how to use it responsibly. An hour spent on your own team's near misses covers that far better than a slide deck full of rules.

Sekcja 6What if it has already happened?

It will happen sooner or later. When it does, act fast:

  1. Rotate any secret that was in the content: API keys, passwords, tokens. Deleting the conversation is not enough, because you don't know where and for how long it's stored,
  2. Delete the conversation, and check whether the account allows training on your chats,
  3. Tell your team lead or whoever handles data protection in your company. If the client's personal data was involved, the client has to hear about it right away, from you. In the EU it's their call whether to report the breach to the national data protection authority, and GDPR gives them only 72 hours for that. Other countries have their own rules and deadlines,
  4. Write down what happened, and figure out which rule was missing.

And the most important part IMO: don't punish people for speaking up. If admitting a mistake gets someone in trouble, the next one won't be admitted, and shadow AI goes even deeper into the shadows.

Sekcja 7Checklist

Before you paste anything into a model, ask yourself:

  • Is this tool approved, and am I on the company account?
  • Are there any secrets in it: keys, passwords, tokens, .env contents?
  • Is there any personal data, and can I pseudonymize it?
  • Does this belong to the client, and did the client agree?
  • Does the model really need this, or would the schema or a minimal example do?

Sekcja 8Summary

Shadow AI is not a story about careless developers. It grows out of deadline pressure, missing rules and approval processes slower than a single sprint. As PARP puts it, it's an organizational problem at least as much as a technical one. And when you work for clients, it's their data and their trust on the line.

If you want to keep it under control, I suggest following the points below:

  • Provide approved tools that are better than the personal ones,
  • Agree on AI rules with your client before the first commit,
  • Send the model only what it needs, and pseudonymize the rest,
  • Fence off secrets from your IDE assistants,
  • Reward reporting instead of punishing it.

I hope you find this article useful. If you have any questions or comments, do not hesitate to post them in the comments. Happy coding!

Udostępnij
Uwagi

O autorze

Staff Software Engineer

Piszę na tym blogu od 2009 roku, a strony i aplikacje webowe tworzę zawodowo od 14 lat. Obecnie w Bishop Fox prowadzę frontend platformy Cosmos, od samego początku projektu. Wcześniej pracowałem m.in. w OLX, KUDO, Auto1 i eZ Systems. Piszę tu o tym, czego nauczyłem się w praktyce: o JavaScripcie, TypeScripcie, Reakcie, CSS-ie i architekturze frontendu. Występowałem też jako prelegent na konferencjach w Polsce i Europie. Masz pytanie albo pomysł na wpis? Daj znać!