Why This Matters Now
AI coding tools are now standard equipment for competitive engineering teams. Cursor, Claude Code, GitHub Copilot, and similar tools are installed on the majority of developer machines at companies that move fast. Yet most teams deployed these tools before security teams understood what they actually transmit.
The answer is: more than most developers realize.
Here are the five most common IP exposure vectors, with real-world examples and exactly how to prevent each one.
Risk 1: Function Names Reveal Business Logic
The most obvious risk is also the most underestimated. When you send code to an LLM for review or refactoring, every function name is transmitted verbatim to a third-party server.
Consider what a function name like getUserPaymentTier reveals:
- -You have a tiered pricing model
- -Payment tiers are tied to user accounts (at the user level, separate from company level)
- -There is a programmatic lookup function (meaning it is called frequently, meaning pricing logic is automated)
A competitor who sees this function name in a training corpus or API log learns your product architecture while your codebase stays unread.
Real example pattern:
// What gets sent to the LLM API (and potentially logged/trained on):
async function getUserPaymentTier(userId: string): Promise<PricingTier> {
const sub = await stripe.subscriptions.list({ customer: userId });
return classifyTier(sub.data[0]?.plan?.interval, sub.data[0]?.quantity);
}
function classifyTier(interval: string, seats: number): PricingTier {
if (interval === 'year' && seats > 50) return 'enterprise';
if (seats > 10) return 'business';
return 'starter';
}
From this single function, a competitor learns: your three pricing tiers (starter, business, enterprise), your enterprise threshold (50 seats), and that you use annual billing as an enterprise qualifier.
How Pretense prevents this:
Pretense mutates identifier names before they leave your machine. The LLM receives:
async function _fn4a2b(_v7c1d: string): Promise<_cls9e3f> {
const _v2b8a = await stripe.subscriptions.list({ customer: _v7c1d });
return _fn6d4e(_v2b8a.data[0]?.plan?.interval, _v2b8a.data[0]?.quantity);
}
function _fn6d4e(_v1a9c: string, _v5f3b: number): _cls9e3f {
if (_v1a9c === 'year' && _v5f3b > 50) return 'enterprise';
if (_v5f3b > 10) return 'business';
return 'starter';
}
The logic is fully preserved. The LLM can still review, refactor, and complete the code correctly, and Pretense swaps the real names back byte-exact in the answer. Meanwhile the naming reveals only stand-ins, so proprietary business logic stays private.
Risk 2: Variable Names Expose Architecture
Variable names are the second most dangerous category. They reveal infrastructure choices, vendor relationships, and internal system topology.
// These variable names reveal: cloud provider, storage vendor,
// internal service topology, and authentication approach
const awsS3BucketName = process.env.STORAGE_BUCKET;
const internalApiEndpoint = 'https://internal.api.company.com';
const redisSessionStore = new RedisClient({ url: process.env.REDIS_URL });
const datadogTracer = require('dd-trace').init({ service: 'payments-api' });
What a threat actor or competitor learns from this context window:
- -You use AWS S3 for storage
- -You have an internal API at a specific subdomain pattern
- -You use Redis for sessions
- -You use Datadog for observability, with a service named 'payments-api'
This is infrastructure reconnaissance delivered via AI API logs.
How Pretense prevents this:
Variable names are mutated with the same deterministic algorithm as function names. awsS3BucketName becomes _v3c7a. The infrastructure details stay on your machine.
Risk 3: Comments Become Training Data
Code comments are the highest-risk single artifact in any codebase. Developers write things in comments they would keep far away from documentation.
// TODO: fix HIPAA violation in this flow: patient IDs being logged to stdout
// HACK: skipping auth check here because legacy API doesn't support tokens
// NOTE: this algorithm is a trade secret: do not publish
// WARNING: Acme Corp data must never touch this endpoint per contract clause 4.2
Every one of these comments represents:
- -A live compliance violation (HIPAA)
- -A known security bypass in production
- -Protected IP with a public filing identifier
- -A contractual obligation that, if violated, creates legal liability
When these comments are sent to an LLM API, they are transmitted to infrastructure outside your control. Training opt-out provisions help, but they are contractual promises; technical controls are a separate layer.
How Pretense prevents this:
Pretense's secret scanner pattern-matches comments against a set of high-risk patterns: PHI references, explicit security bypass markers, IP filing references, and customer-identifying terms. Flagged comments are surfaced before transmission, any secrets inside them are swapped for stand-ins, and the developer is notified with the reason.
Risk 4: Secrets in Context Windows
This risk is more subtle than pasting an API key into a prompt. It is about what the LLM infers from surrounding code.
You probably keep your .env file out of prompts. But you send code like this:
const openaiClient = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
organization: process.env.OPENAI_ORG_ID,
});
const anthropicClient = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY,
});
const stripeClient = Stripe(process.env.STRIPE_SECRET_KEY);
The LLM now knows you have API keys for OpenAI, Anthropic, and Stripe in your environment. This is vendor intelligence. More dangerously, if a developer has previously sent a partially populated .env to a different AI session, and the LLM provider correlates sessions (even anonymized), inference attacks become possible.
The direct risk is simpler: developers regularly autocomplete environment variable names in test files, and those completions reflect real key names from their environment.
// What a developer types:
const testApiKey = process.env.
// What autocomplete suggests (based on context from prior sessions):
const testApiKey = process.env.STRIPE_SECRET_KEY_PROD;
How Pretense prevents this:
Pretense's secret scanner detects over 30 patterns of credentials, tokens, and keys. Any secret matching a known pattern is swapped for a deterministic stand-in before the context window is transmitted, and it stays a stand-in in the answer by design. Environment variable references are flagged for review.
Risk 5: Refactoring Requests Expose Complete Internal APIs
The highest-volume risk for teams actively using AI tools is refactoring. When a developer asks Claude or Copilot to refactor a service, they typically provide the full service file as context.
A complete service file contains:
- -Every method signature (the full internal API surface)
- -Every dependency injection point (the service graph)
- -Every database query pattern (the data model)
- -Every error handling path (the failure modes)
// A typical refactoring context window reveals:
class PaymentOrchestrationService {
constructor(
private readonly paymentGateway: StripeGateway,
private readonly fraudDetection: AcmeFraudAPI, // vendor name
private readonly settlementQueue: BullQueue,
private readonly auditLogger: ComplianceAuditLogger,
private readonly notificationService: TwilioNotifier // vendor name
) {}
async processPayment(request: PaymentRequest): Promise<PaymentResult> { ... }
async retryFailedPayment(paymentId: string): Promise<void> { ... }
async reconcileSettlement(batchId: string): Promise<ReconciliationReport> { ... }
async generateAuditReport(startDate: Date, endDate: Date): Promise<AuditLog> { ... }
}
This is your complete payment orchestration architecture, vendor relationships, and API contract, delivered to a third-party server in a single refactoring request.
How Pretense prevents this:
Class names, constructor parameter names, method names, and injected service names are all mutated. The LLM sees the structural pattern and can refactor it correctly. It sees only stand-ins in place of AcmeFraudAPI, ComplianceAuditLogger, and TwilioNotifier.
The Bottom Line
Each of these risks plays out in everyday use, well before any breach. They require only that AI provider infrastructure is accessible to parties outside your authorization, which is always true by definition when you send API requests to a third party.
The five risks above are practical, everyday realities. They are the normal usage patterns of every engineering team that uses AI coding tools.
Pretense addresses all five with a single proxy layer that adds under 2ms of latency to every request.
How I run Pretense on my own code
I build Pretense with Claude Code, and my sessions run through it. Here is the setup I give a new engineer, in order.
- -Run the agent through the proxy. Type
pretense run claudewhere you would typeclaude. It starts a local proxy on port 9339, points Claude Code at it and stops the proxy when the session ends. Detected secrets and PII leave the laptop as stand-in tokens. - -Put hooks on every repo.
pretense installadds a pre-commit and a pre-push hook. A key that never lands in git is a key an agent never reads. - -Scan before an agent reads the repo.
pretense scan path .lists what it finds. In CI,pretense scan ciexits 0 when clean, 2 on findings and 3 when it examined nothing. Treat 3 as a failure. - -Preview the swap.
pretense review .shows what would be mutated.pretense mutate <file> --previewprints one file mutated and leaves the file on disk as it was. - -Round-trip a file you need to share.
pretense mutate <file> --map <map>swaps the secrets in place.pretense reverse <file> --map <map>turns it back into the original, byte for byte, from the encrypted map on your machine. We run that round trip on real files before every CLI release and compare sha256 hashes.
One thing I learned the hard way: inside a session the agent sees stand-ins, so a file the agent writes can hold a stand-in where you expected a real value. Grep what the agent wrote before you ship it.
What Pretense will not do is put a secret back into the model's answer. Code identifiers come back byte-exact. Secrets and PII stay as stand-ins in the response. That is deliberate: a prompt-injected model can echo a token into a link, and reversing it there would hand over the real key.
What this means if you buy security
The outcome: your engineers keep their AI coding tools, and detected secrets and PII leave each laptop as stand-in tokens.
- -The control sits on the endpoint. The swap runs on the developer's machine before the request leaves. No shared gateway holds every team's keys.
- -You get evidence, not a memo.
pretense audit --format csvexports every mutation event. Add--framework SOC2to keep only the events that map to that framework. - -Check it on your own code. Detection is pattern and entropy based, and coverage varies by data type. Run
pretense review .on a real repo and read what it catches before you sign off. - -Start with one team. One repo,
pretense run claude, one week. Then read the audit log with the team lead.
Try it on your own repo: create a free account, then run npm i -g @pretense/cli and pretense run claude. If you need a security review first, talk to me.