@mullion/eslint-plugin
v0.2.3
Published
ESLint rules for type-safe LLM context management
Maintainers
Readme
Installation
npm install @mullion/eslint-plugin --save-devOverview
This ESLint plugin provides static analysis rules to catch context leaks and enforce confidence checking in Mullion applications. It helps prevent security vulnerabilities by detecting when LLM-generated values cross scope boundaries without proper bridging.
Why Use This?
LLM applications deal with data that crosses trust boundaries. Without static analysis, it's easy to accidentally:
- Leak privileged data into public scopes (admin → customer)
- Mix tenant data in multi-tenant systems
- Use low-confidence outputs for critical decisions without validation
- Lose audit trails when data flows between contexts
This plugin catches these issues at compile-time with zero runtime overhead.
What It Prevents
| Risk | Without Plugin | With Plugin | | -------------------- | --------------------------------- | --------------------- | | Context leaks | Discovered in production | Caught in IDE/CI | | Low confidence usage | Runtime errors or silent failures | Compile-time warnings | | Audit trail gaps | Manual code review needed | Automatic enforcement | | Security review time | Hours per PR | Seconds (automated) |
Real-World Impact
// ❌ Without plugin: This compiles but leaks admin data
let adminData;
await client.scope('admin', async (ctx) => {
adminData = await ctx.infer(SecretSchema, sensitiveDoc);
});
await client.scope('public', async (ctx) => {
return adminData.value; // BUG: No warning!
});
// ✅ With plugin: ESLint error prevents compilation
// "Context leak detected: 'adminData' crosses scope boundary"Quick Setup
Flat Config (ESLint 9+)
// eslint.config.js
import mullion from '@mullion/eslint-plugin';
export default [
{
plugins: {
'@mullion': mullion,
},
rules: {
'@mullion/no-context-leak': 'error',
'@mullion/require-confidence-check': 'warn',
},
},
];Legacy Config (.eslintrc)
{
"plugins": ["@mullion"],
"rules": {
"@mullion/no-context-leak": "error",
"@mullion/require-confidence-check": "warn"
}
}Using Preset Configs
// Use recommended configuration
import mullion from '@mullion/eslint-plugin';
export default [
...mullion.configs.recommended
];
// Or use strict configuration
export default [
...mullion.configs.strict
];Rules
no-context-leak (🚨 Error)
Prevents accidental context leaks when LLM-generated values cross scope boundaries without proper bridging.
❌ Incorrect
let leaked;
await client.scope('admin', async (ctx) => {
leaked = await ctx.infer(Schema, 'secret data'); // 🚨 ESLint error
});
await client.scope('public', async (ctx) => {
return leaked.value; // 🚨 ESLint error
});await client.scope('scope-a', async (ctxA) => {
const dataA = await ctxA.infer(Schema, input);
await client.scope('scope-b', async (ctxB) => {
return dataA.value; // 🚨 ESLint error - cross-scope usage
});
});✅ Correct
await client.scope('admin', async (adminCtx) => {
const adminData = await adminCtx.infer(Schema, 'secret data');
await client.scope('public', async (publicCtx) => {
const bridged = publicCtx.bridge(adminData); // ✅ Explicit bridge
return bridged.value;
});
});await client.scope('scope-a', async (ctxA) => {
const dataA = await ctxA.infer(Schema, input);
return ctxA.use(dataA); // ✅ Safe - same scope
});require-confidence-check (⚠️ Warning)
Warns when LLM-generated values are used without checking their confidence scores.
❌ Triggers Warning
await client.scope('processing', async (ctx) => {
const result = await ctx.infer(Schema, input);
// ⚠️ ESLint warning - using value without confidence check
if (result.value.category === 'important') {
processImportantData(result.value);
}
return result.value; // ⚠️ ESLint warning
});✅ Correct
await client.scope('processing', async (ctx) => {
const result = await ctx.infer(Schema, input);
// ✅ Check confidence before use
if (result.confidence >= 0.8 && result.value.category === 'important') {
processImportantData(result.value);
}
// ✅ Or use a handler function
return handleResult(result); // Function receives full Owned object
});Configuration
Rule Options
no-context-leak
{
"@mullion/no-context-leak": ["error", {
"allowBridge": true, // Allow ctx.bridge() calls (default: true)
"strictMode": false // Stricter checking (default: false)
}]
}require-confidence-check
{
"@mullion/require-confidence-check": ["warn", {
"minConfidence": 0.8, // Minimum confidence threshold
"requireExplicit": false, // Require explicit .confidence checks
"allowHandlers": true // Allow functions that handle Owned values
}]
}Preset Configurations
recommended
{
rules: {
'@mullion/no-context-leak': 'error',
'@mullion/require-confidence-check': 'warn'
}
}strict
{
rules: {
'@mullion/no-context-leak': 'error',
'@mullion/require-confidence-check': 'error' // More strict
}
}Examples
Real-World Violations
Customer Support Pipeline
// ❌ BAD: Customer data leaks to admin scope
let customerQuery;
await client.scope('customer', async (ctx) => {
customerQuery = await ctx.infer(QuerySchema, userInput); // 🚨 Leak
});
await client.scope('admin', async (ctx) => {
// This could expose customer PII to admin systems
return analyzeWithAdminTools(customerQuery.value); // 🚨 Leak
});
// ✅ GOOD: Explicit bridging
await client.scope('customer', async (customerCtx) => {
const query = await customerCtx.infer(QuerySchema, userInput);
return await client.scope('admin', async (adminCtx) => {
const bridged = adminCtx.bridge(query); // ✅ Tracked transfer
return analyzeWithAdminTools(bridged.value);
});
});Multi-Tenant Data Processing
// ❌ BAD: Tenant data cross-contamination
const results = [];
for (const tenant of tenants) {
await client.scope(`tenant-${tenant.id}`, async (ctx) => {
const data = await ctx.infer(Schema, tenant.input);
results.push(data); // 🚨 Mixing tenant scopes!
});
}
// ✅ GOOD: Keep tenant data isolated
const results = [];
for (const tenant of tenants) {
const result = await client.scope(`tenant-${tenant.id}`, async (ctx) => {
const data = await ctx.infer(Schema, tenant.input);
return ctx.use(data); // ✅ Extract safely within scope
});
results.push({tenantId: tenant.id, data: result});
}Confidence Checking Patterns
❌ Common Anti-Patterns
// Direct value access without confidence check
const result = await ctx.infer(Schema, input);
const decision = result.value.decision; // ⚠️ Warning
// Implicit confidence in conditional
if (result.value.important) {
// ⚠️ Warning
processImportant();
}✅ Good Patterns
// Explicit confidence checking
const result = await ctx.infer(Schema, input);
if (result.confidence >= 0.9) {
const decision = result.value.decision; // ✅ Safe
}
// Handler function approach
function handleClassification(owned: Owned<Classification, string>) {
if (owned.confidence < 0.8) {
return escalateToHuman(owned);
}
return processAutomatically(owned.value);
}
const result = await ctx.infer(Schema, input);
handleClassification(result); // ✅ Handler receives full contextTypeScript Integration
The plugin leverages TypeScript's type system for accurate detection:
// The plugin understands scope types
type AdminData = Owned<Secret, 'admin'>;
type UserData = Owned<Public, 'user'>;
// It detects type mismatches
function processUserData(data: UserData) {
/* ... */
}
const adminData: AdminData = await adminCtx.infer(SecretSchema, input);
processUserData(adminData); // 🚨 Type and scope mismatch detectedTroubleshooting
Common Issues
False Positives
If you get false positives, you might need to:
Update TypeScript configuration:
{ "parserOptions": { "project": "./tsconfig.json" } }Ensure proper imports:
import type {Owned, Context} from '@mullion/core';
Missing Violations
If leaks aren't being caught:
- Check TypeScript types are available
- Verify the plugin can access type information
- Ensure you're using the correct scope patterns
Debugging
Enable debug mode to see what the plugin is detecting:
DEBUG=@mullion/eslint-plugin eslint your-file.tsExamples in Action
See the basic example which demonstrates both correct usage and intentional violations that ESLint catches.
Run the example:
cd examples/basic
npm install
npm run lint # See violations caught
npm run demo # See proper usageContributing
Found a bug or want to add a rule? See CONTRIBUTING.md for guidelines.
License
MIT - see LICENSE for details.
