npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@leakguard/guard

v3.0.2

Published

Runtime protection against secret leakage in responses and logs

Readme

@leakguard/guard

🛡️ Runtime protection against secret leakage in responses and logs

Automatically detect and redact sensitive information like API keys, JWTs, private keys, and more from HTTP responses and application logs.

Why @leakguard/guard?

The Problem

Sensitive data frequently leaks through:

  • API responses containing database records with secrets
  • Error messages exposing connection strings and tokens
  • Application logs recording sensitive request/response data
  • Debug output showing internal configuration
  • Stack traces containing environment variables

Why It's Hard

  • Pattern complexity: Secrets come in many formats (API keys, JWTs, private keys, etc.)
  • Performance impact: Scanning every response can slow down your API
  • Framework differences: Each web framework handles responses differently
  • False positives: Distinguishing secrets from legitimate data
  • Logger integration: Different logging libraries have different APIs

Why Not Roll Your Own?

// ❌ Incomplete pattern matching
if (response.includes('api_key')) {
  response = response.replace(/api_key=\w+/g, 'api_key=[REDACTED]');
}

// ❌ Misses many secret types
// What about JWTs? Private keys? Database URLs? Credit cards?

// ❌ Performance problems  
JSON.stringify(response).replace(/secret/g, '[REDACTED]'); // Scans everything

// ❌ No framework integration
// Manual response interception in every route

// ✅ LeakGuard handles all of this correctly

Installation

npm install @leakguard/guard

# Peer dependencies for specific frameworks
npm install express @types/express          # For Express
npm install @nestjs/common @nestjs/core     # For NestJS
npm install pino                            # For Pino logger
npm install winston                         # For Winston logger

Quick Start

Express

import express from 'express';
import { expressSecretGuard } from '@leakguard/guard';

const app = express();

// Protect all responses from secret leakage
app.use(expressSecretGuard({
  mode: 'redact', // or 'block'
  skipRoutes: ['/health', '/metrics']
}));

app.get('/api/config', (req, res) => {
  res.json({
    apiKey: 'sk_live_secret123',           // 🔒 Will be redacted
    databaseUrl: 'postgres://user:pass@host/db', // 🔒 Will be redacted
    publicConfig: 'safe to expose',        // ✅ Will remain visible
    debugMode: true                        // ✅ Will remain visible
  });
});

// Response will be:
// {
//   "apiKey": "[REDACTED_API_KEY:sk***23]",
//   "databaseUrl": "[REDACTED_CONNECTION_STRING:po***db]", 
//   "publicConfig": "safe to expose",
//   "debugMode": true
// }

NestJS

import { Controller, Get, UseInterceptors } from '@nestjs/common';
import { SecretGuardInterceptor } from '@leakguard/guard';

@Controller('api')
@UseInterceptors(new SecretGuardInterceptor({
  mode: 'redact',
  allowlist: ['public-api-key'] // Don't redact specific values
}))
export class ApiController {
  @Get('config')
  getConfig() {
    return {
      privateKey: process.env.PRIVATE_KEY, // 🔒 Will be redacted
      jwt: getAuthToken(),                  // 🔒 Will be redacted
      publicEndpoint: '/api/public'         // ✅ Will remain visible
    };
  }
  
  @Get('user/:id')
  getUser(@Param('id') id: string) {
    // Even if user object contains secrets from database,
    // they will be automatically redacted 🔒
    return this.userService.findById(id);
  }
}

Logger Protection

import { wrapGlobalConsole, createSecurePinoLogger } from '@leakguard/guard';
import pino from 'pino';

// Option 1: Wrap global console
wrapGlobalConsole({ mode: 'redact' });

console.log('API Key:', 'sk_live_secret123');
// Output: API Key: [REDACTED_API_KEY:sk***23]

// Option 2: Wrap specific logger  
const logger = pino();
const secureLogger = createSecurePinoLogger(logger, { mode: 'redact' });

secureLogger.info({
  user: 'john',
  apiKey: 'sk_live_secret123',    // 🔒 Will be redacted
  action: 'login'                 // ✅ Will remain visible
});

Detection Capabilities

Automatically Detected Secrets

API Keys

  • AWS Access Keys (AKIA...)
  • Stripe Keys (sk_live_..., pk_live_...)
  • Google API Keys (AIza...)
  • GitHub Tokens (ghp_...)
  • Generic API key patterns

Authentication Tokens

  • JWT Tokens (eyJ...)
  • Bearer Tokens
  • OAuth tokens
  • Session tokens

Private Keys

  • RSA Private Keys
  • EC Private Keys
  • DSA Private Keys
  • OpenSSH Private Keys

Database & Connection Strings

  • MongoDB URLs
  • PostgreSQL URLs
  • MySQL URLs
  • Redis URLs

Sensitive Personal Data

  • Credit Card Numbers
  • US Social Security Numbers
  • Phone numbers (configurable)

High Entropy Strings

  • Cryptographic keys
  • Random tokens
  • Base64 encoded secrets

Pattern Examples

// These will be automatically detected and redacted:

"sk_live_51234567890abcdef"           // Stripe API key
"AIzaSyB1234567890abcdef"             // Google API key  
"ghp_1234567890abcdefghijklmnop"      // GitHub personal access token
"AKIAIOSFODNN7EXAMPLE"                // AWS access key

"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.signature"  // JWT

"-----BEGIN RSA PRIVATE KEY-----\n..."  // Private key

"mongodb://user:password@host:27017/db"  // Database URL
"postgres://user:pass@localhost/db"      // PostgreSQL URL

"Bearer sk_live_1234567890abcdef"        // Bearer token

"4111 1111 1111 1111"                    // Credit card
"123-45-6789"                           // SSN

// High entropy detection (when enabled)
"Ak9f8s7d6g5h4j3k2l1m0n9b8v7c6x5z"    // Random string

Configuration Options

Basic Configuration

import { CoreSecretDetector, ContentRedactor } from '@leakguard/guard';

// Create detector with custom config
const detector = new CoreSecretDetector({
  // Enable/disable entropy-based detection
  enableEntropyDetection: true,
  entropyThreshold: 4.5,        // Higher = more strict
  
  // Allowlist specific values
  allowlist: [
    'public-api-key',
    'demo-token-123',
    'localhost'  
  ],
  
  // Custom secret patterns
  patterns: [
    {
      name: 'custom_secret',
      pattern: /CUSTOM_[A-Z0-9]{16}/g,
      confidence: 0.9,
      description: 'Custom secret format'
    }
  ]
});

// Create redactor
const redactor = new ContentRedactor({
  mode: 'redact', // or 'block'
  
  // Custom redaction function
  customRedactor: (content) => {
    return typeof content === 'string' 
      ? '***CUSTOM REDACTED***'
      : { error: 'Sensitive data removed' };
  }
});

Framework-Specific Options

Express Configuration

import { expressSecretGuard } from '@leakguard/guard';

app.use(expressSecretGuard({
  mode: 'redact',
  
  // Skip specific routes
  skipRoutes: ['/health', '/metrics', '/public/*'],
  
  // Skip specific HTTP methods  
  skipMethods: ['GET', 'OPTIONS'],
  
  // Custom handling
  onBlock: (req, res, next) => {
    res.status(500).json({
      error: 'Response blocked due to sensitive content',
      timestamp: new Date().toISOString()
    });
  },
  
  // Secret detection callback
  onSecretDetected: (detection, context) => {
    console.warn('Secret detected:', {
      secretCount: detection.detectedSecrets.length,
      types: detection.detectedSecrets.map(s => s.type),
      route: context.url,
      method: context.method
    });
  }
}));

NestJS Configuration

import { SecretGuardInterceptor, createSecretGuardInterceptor } from '@leakguard/guard';

// Option 1: Use directly
@UseInterceptors(new SecretGuardInterceptor({
  mode: 'redact',
  skipRoutes: ['/health'],
  onSecretDetected: (detection, context) => {
    // Log to your monitoring system
  }
}))

// Option 2: Create configured class
const CustomSecretGuard = createSecretGuardInterceptor({
  mode: 'block',
  skipMethods: ['GET']
});

@UseInterceptors(CustomSecretGuard)
@Controller('sensitive')
export class SensitiveController {
  // Will be protected
}

Logger Configuration

// Console wrapper
import { SecureConsole, wrapGlobalConsole } from '@leakguard/guard';

// Replace global console
wrapGlobalConsole({
  mode: 'redact',
  skipLevels: ['debug'],          // Don't scan debug logs
  onSecretDetected: (detection) => {
    // Alert on secrets in logs
    sendSecurityAlert(detection);
  }
});

// Create secure console instance
const secureConsole = new SecureConsole({
  mode: 'redact'
});

secureConsole.log('User token:', userToken); // Automatically redacted
// Pino logger wrapper
import { createSecurePinoLogger } from '@leakguard/guard';
import pino from 'pino';

const logger = pino();
const secureLogger = createSecurePinoLogger(logger, {
  mode: 'redact',
  allowlist: ['public-values']
});

secureLogger.info({
  user: 'john',
  apiKey: 'secret123',        // 🔒 Redacted
  action: 'login'             // ✅ Preserved
});
// Winston logger wrapper  
import { createSecureWinstonLogger } from '@leakguard/guard';
import winston from 'winston';

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  transports: [new winston.transports.Console()]
});

const secureLogger = createSecureWinstonLogger(logger, {
  mode: 'redact'
});

secureLogger.error('Database connection failed', {
  connectionString: 'postgres://user:pass@host/db' // 🔒 Redacted
});

Redaction Modes

Redact Mode (Default)

Replaces secrets with redacted placeholders while preserving data structure:

// Input
{
  "user": "john",
  "apiKey": "sk_live_1234567890abcdefghij",
  "config": {
    "jwt": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.payload.signature"
  }
}

// Output (redacted)
{
  "user": "john",
  "apiKey": "[REDACTED_API_KEY:sk***ij]",
  "config": {
    "jwt": "[REDACTED_JWT:ey***re]"
  }
}

Block Mode

Blocks the entire response/log entry when secrets are detected:

const config = { mode: 'block' };

// Input with secrets → Output
null // Entire response/log blocked

// Clean input → Output  
{ "clean": "data" } // Passed through unchanged

Custom Redaction

Implement your own redaction logic:

const config = {
  mode: 'redact',
  customRedactor: (content) => {
    if (typeof content === 'string') {
      return '***SENSITIVE***';
    }
    
    if (typeof content === 'object') {
      return { 
        message: 'This response contained sensitive data',
        timestamp: new Date().toISOString()
      };
    }
    
    return '[BLOCKED]';
  }
};

Performance

Benchmarks

  • Clean content (fast path): 10,000+ ops/sec
  • Content with secrets: 1,000-5,000 ops/sec
  • Large objects (1MB): ~500 ops/sec
  • Memory overhead: <5% for typical payloads

Zero-Cost Fast Path

LeakGuard is optimized for clean content:

// ✅ Fast path - no secrets detected
const cleanResponse = { user: 'john', status: 'active' };
// → Nearly zero overhead

// 🔍 Scan path - secrets detected  
const secretResponse = { user: 'john', apiKey: 'sk_live_123' };
// → Full pattern matching and redaction

Performance Tips

  1. Use allowlists to skip known safe values
  2. Disable entropy detection if not needed (faster)
  3. Skip safe routes like health checks
  4. Use redact mode instead of block mode (faster)
// ✅ Optimized configuration
const optimizedConfig = {
  mode: 'redact',
  enableEntropyDetection: false,  // Faster
  skipRoutes: ['/health', '/metrics', '/static/*'],
  skipMethods: ['GET'],           // Skip read-only endpoints
  allowlist: ['public-key', 'demo-token']
};

Security Best Practices

1. Use Allowlists for Known Safe Values

const config = {
  allowlist: [
    'demo-api-key',           // Demo/test keys
    'public-stripe-key',      // Publishable keys  
    'localhost',              // Local development
    'development-token'       // Non-production secrets
  ]
};

2. Configure Appropriate Skip Rules

const config = {
  // Skip high-traffic, low-risk endpoints
  skipRoutes: [
    '/health',
    '/metrics', 
    '/static/*',
    '/public/*'
  ],
  
  // Skip read-only methods (if secrets unlikely in responses)
  skipMethods: ['GET', 'HEAD', 'OPTIONS']
};

3. Monitor Secret Detection Events

const config = {
  onSecretDetected: (detection, context) => {
    // Send to monitoring/alerting system
    monitoringService.recordEvent('secret_detected', {
      secretTypes: detection.detectedSecrets.map(s => s.type),
      route: context?.url,
      method: context?.method,
      timestamp: new Date().toISOString()
    });
    
    // Alert on high-confidence secrets
    const highConfidenceSecrets = detection.detectedSecrets
      .filter(s => s.confidence > 0.8);
      
    if (highConfidenceSecrets.length > 0) {
      alertingService.sendAlert('high_confidence_secret_leak', {
        secrets: highConfidenceSecrets,
        context
      });
    }
  }
};

4. Use Block Mode for Critical Endpoints

// Block mode for admin/sensitive endpoints
app.use('/admin/*', expressSecretGuard({ mode: 'block' }));

// Redact mode for regular API endpoints  
app.use('/api/*', expressSecretGuard({ mode: 'redact' }));

5. Protect Logs in Production

// Only wrap console in production
if (process.env.NODE_ENV === 'production') {
  wrapGlobalConsole({ 
    mode: 'redact',
    // More aggressive scanning in production
    enableEntropyDetection: true
  });
}

Custom Patterns

Adding Custom Secret Types

import { DEFAULT_PATTERNS, CoreSecretDetector } from '@leakguard/guard';

const customPatterns = [
  {
    name: 'internal_token',
    pattern: /INT_[A-Z0-9]{32}/g,
    confidence: 0.95,
    description: 'Internal system token'
  },
  
  {
    name: 'employee_id',
    pattern: /EMP\d{8}/g,
    confidence: 0.8,
    description: 'Employee identifier'
  },
  
  {
    name: 'api_secret',
    pattern: /secret_[a-z0-9]{24}/gi,
    confidence: 0.9,
    description: 'API secret key'
  }
];

const detector = new CoreSecretDetector({
  patterns: [...DEFAULT_PATTERNS, ...customPatterns]
});

Entropy-Based Detection

const detector = new CoreSecretDetector({
  enableEntropyDetection: true,
  entropyThreshold: 4.5,      // Higher = more strict
  
  // Fine-tune entropy detection
  minLength: 16,              // Minimum string length to check
  maxLength: 100              // Maximum string length to check
});

// Will detect high-randomness strings like:
// "Ak9f8s7d6g5h4j3k2l1m0n9b8v7c6x5z" // High entropy
// "aaaaaaaaaaaaaaaaaaaa"              // Low entropy (ignored)

Context-Aware Patterns

const detector = new CoreSecretDetector({
  patterns: [
    {
      name: 'admin_session',
      pattern: /admin_sess_[a-f0-9]{40}/g,
      confidence: 1.0,
      description: 'Admin session token'
    }
  ],
  
  // Custom detection based on context
  contextualDetection: (content, context) => {
    // More aggressive detection for admin routes
    if (context?.url?.includes('/admin/')) {
      return {
        enableEntropyDetection: true,
        entropyThreshold: 3.5  // Lower threshold = more sensitive
      };
    }
    
    return {};
  }
});

Troubleshooting

Common Issues

1. False Positives

// Issue: Legitimate data being redacted
const response = { orderId: 'ORDER_1234567890ABCDEF' }; // Looks like API key

// ✅ Solution: Use allowlist
const config = {
  allowlist: ['ORDER_', 'USER_', 'TEMP_'] // Prefixes to ignore
};

// ✅ Alternative: Adjust pattern confidence
const customPatterns = DEFAULT_PATTERNS.map(pattern => ({
  ...pattern,
  confidence: pattern.name === 'api_key' ? 0.95 : pattern.confidence
}));

2. Performance Issues

// Issue: Slow response times

// ✅ Debug performance
const detector = new CoreSecretDetector({
  onDetectionStart: () => console.time('detection'),
  onDetectionEnd: () => console.timeEnd('detection')
});

// ✅ Optimize configuration
const optimizedConfig = {
  enableEntropyDetection: false,     // Disable if not needed
  skipLargeObjects: true,            // Skip objects > 1MB
  skipMethods: ['GET'],              // Skip read-only endpoints
  patterns: customPatterns.slice(0, 5) // Use fewer patterns
};

3. Missing Secrets

// Issue: Expected secrets not being detected

// ✅ Debug detection
const detector = new CoreSecretDetector();
const result = detector.detect(content);

console.log('Detection result:', {
  hasSecrets: result.hasSecrets,
  secretCount: result.detectedSecrets.length,
  detectedTypes: result.detectedSecrets.map(s => s.type)
});

// ✅ Test specific patterns
DEFAULT_PATTERNS.forEach(pattern => {
  pattern.pattern.lastIndex = 0; // Reset regex
  if (pattern.pattern.test(content)) {
    console.log('Matched pattern:', pattern.name);
  }
});

4. Framework Integration Issues

// Issue: Middleware not working

// ✅ Check middleware order
app.use(express.json());           // Must be before guard
app.use(expressSecretGuard({}));   // Guard processes parsed body

// ✅ Debug request processing  
app.use('/api', (req, res, next) => {
  console.log('Request body type:', typeof req.body);
  console.log('Body content:', req.body);
  next();
}, expressSecretGuard({}));

// ✅ Check response interception
const originalJson = res.json;
res.json = function(body) {
  console.log('Response body:', body);
  return originalJson.call(this, body);
};

API Reference

Core Classes

// Secret detection
class CoreSecretDetector {
  constructor(config?: GuardConfig)
  detect(content: any, context?: any): SecretDetectionResult
}

// Content redaction
class ContentRedactor {
  constructor(config?: GuardConfig)
  redactContent(content: any, detection: SecretDetectionResult): {
    redacted: any;
    blocked: boolean;
  }
}

Framework Adapters

// Express
function expressSecretGuard(config?: ExpressGuardOptions): RequestHandler

// NestJS  
class SecretGuardInterceptor implements NestInterceptor
function createSecretGuardInterceptor(config?: NestGuardOptions): typeof SecretGuardInterceptor

Logger Wrappers

// Console
class SecureConsole
function wrapGlobalConsole(config?: LoggerGuardOptions): void

// Pino
function createSecurePinoLogger(logger: any, config?: LoggerGuardOptions): any

// Winston
function createSecureWinstonLogger(logger: any, config?: LoggerGuardOptions): any

Types

interface GuardConfig {
  mode: 'redact' | 'block';
  patterns?: SecretPattern[];
  allowlist?: string[];
  enableEntropyDetection?: boolean;
  entropyThreshold?: number;
  onSecretDetected?: (detection: SecretDetectionResult, context?: any) => void;
  customRedactor?: (content: any) => any;
}

interface SecretDetectionResult {
  hasSecrets: boolean;
  detectedSecrets: DetectedSecret[];
  redactedContent?: any;
}

interface DetectedSecret {
  type: string;
  field?: string;
  value: string;
  start: number;
  end: number;
  confidence: number;
}

Examples

E-commerce API Protection

import express from 'express';
import { expressSecretGuard } from '@leakguard/guard';

const app = express();

// Protect customer data endpoints
app.use('/api/customers', expressSecretGuard({
  mode: 'redact',
  
  // E-commerce specific patterns
  patterns: [
    {
      name: 'credit_card',
      pattern: /\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/g,
      confidence: 0.8
    },
    {
      name: 'payment_token',
      pattern: /pay_[a-zA-Z0-9]{24}/g,
      confidence: 0.95
    }
  ],
  
  onSecretDetected: (detection, context) => {
    // Alert on PII exposure
    if (detection.detectedSecrets.some(s => s.type === 'credit_card')) {
      securityAlert('PII_EXPOSURE', { route: context.url });
    }
  }
}));

app.get('/api/customers/:id', async (req, res) => {
  const customer = await db.customers.findById(req.params.id);
  // Credit cards, payment tokens automatically redacted 🔒
  res.json(customer);
});

Microservice Logging

import { createSecurePinoLogger, wrapGlobalConsole } from '@leakguard/guard';
import pino from 'pino';

// Service-wide console protection
wrapGlobalConsole({
  mode: 'redact',
  allowlist: ['service-name', 'public-endpoints']
});

// Structured logging with protection
const logger = pino();
const secureLogger = createSecurePinoLogger(logger, {
  mode: 'redact',
  
  // Microservice specific patterns
  patterns: [
    {
      name: 'service_token',
      pattern: /svc_[a-z]{8}_[a-f0-9]{32}/g,
      confidence: 0.9
    }
  ]
});

// Usage throughout application
secureLogger.info('User authenticated', {
  userId: '12345',
  serviceToken: 'svc_auth_abc123...',  // 🔒 Redacted
  endpoint: '/api/auth'                // ✅ Preserved  
});

console.log('Database connected:', dbConnectionString); // 🔒 Redacted

Admin Panel Security

import { SecretGuardInterceptor } from '@leakguard/guard';

// Strict protection for admin endpoints
const AdminSecretGuard = createSecretGuardInterceptor({
  mode: 'block',              // Block any response with secrets
  enableEntropyDetection: true,
  entropyThreshold: 3.0,      // Very sensitive
  
  onSecretDetected: (detection, context) => {
    // Immediate security alert for admin endpoints
    securityIncident('ADMIN_SECRET_EXPOSURE', {
      secrets: detection.detectedSecrets,
      admin: context.user?.id,
      timestamp: new Date().toISOString()
    });
  }
});

@Controller('admin')
@UseInterceptors(AdminSecretGuard)
export class AdminController {
  @Get('system/config')
  getSystemConfig() {
    // Any response with secrets will be blocked 🚫
    return systemConfig;
  }
  
  @Get('users/:id/details') 
  getUserDetails(@Param('id') id: string) {
    // Comprehensive user data (may contain secrets) 🚫
    return userService.getFullUserDetails(id);
  }
}

License

MIT - see LICENSE for details.