ESLint InterlaceESLint Interlace
Plugin: operabilityRules

no-verbose-error-messages

ESLint rule documentation for no-verbose-error-messages

📡 Live from GitHub — This documentation is fetched directly from no-verbose-error-messages.md and cached for 6 hours.

Keywords: stack trace, error message, information disclosure, CWE-209, production, API response CWE: CWE-209
OWASP: A01:2021-Broken Access Control

Prevent exposing stack traces to users in API responses

Prevents exposing stack traces and verbose error details to users through API responses. This rule is part of eslint-plugin-operability and provides LLM-optimized error messages.

⚠️ Quality/Security rule | 📋 Set to warn in recommended

Quick Summary

AspectDetails
CWE ReferenceCWE-209 (Info in Errors)
SeverityMedium (information disclosure)
Auto-Fix❌ No auto-fix (requires safe error handling)
CategoryQuality / Operability
Best ForExpress/Fastify/Koa applications with API endpoints

Vulnerability and Risk

Vulnerability: Exposing stack traces in API responses reveals internal application structure, file paths, library versions, and code organization to potential attackers.

Risk: Verbose error messages help attackers:

  • Map internal application structure
  • Identify vulnerable dependencies
  • Craft targeted exploits
  • Understand deployment environment

Rule Details

This rule detects:

  • res.send(error.stack) - directly sending stack traces
  • res.json({ stack: error.stack }) - stack traces in JSON responses
  • Response objects containing stack properties

Why This Matters

RiskImpactSolution
🔍 ReconnaissanceAttackers learn internal detailsReturn generic error messages
📂 Path DisclosureFile paths reveal deployment structureLog details server-side only
📚 Version LeakLibrary versions expose vulnerabilitiesUse error codes, not raw errors

Configuration

This rule has no configuration options.

{
  rules: {
    'operability/no-verbose-error-messages': 'error'
  }
}

Examples

❌ Incorrect

app.use((err, req, res, next) => {
  // Sending stack trace directly
  res.status(500).send(err.stack); // ❌ Stack trace exposed
});

app.get('/api/data', async (req, res) => {
  try {
    const data = await getData();
    res.json(data);
  } catch (error) {
    // Stack trace in JSON response
    res.status(500).json({
      message: error.message,
      stack: error.stack, // ❌ Stack trace in response
    });
  }
});

// Error object with stack property
app.use((err, req, res, next) => {
  res.json({
    error: true,
    stack: err.stack, // ❌ Exposed
    path: __dirname, // ❌ Also exposed
  });
});

✅ Correct

app.use((err, req, res, next) => {
  // Log full error server-side
  console.error('Request failed:', {
    // ✅ Server-side logging
    error: err,
    stack: err.stack,
    requestId: req.id,
  });

  // Return generic message to client
  res.status(500).json({
    error: 'Internal server error', // ✅ Generic message
    requestId: req.id, // ✅ Correlation ID for support
  });
});

// Better: Custom error handler with error codes
class AppError extends Error {
  constructor(
    public code: string,
    public statusCode: number,
    message: string,
  ) {
    super(message);
  }
}

app.use((err, req, res, next) => {
  console.error(err); // ✅ Full error logged server-side

  if (err instanceof AppError) {
    res.status(err.statusCode).json({
      code: err.code, // ✅ User-facing code
      message: err.message, // ✅ Safe message
    });
  } else {
    res.status(500).json({
      code: 'INTERNAL_ERROR',
      message: 'An unexpected error occurred',
    });
  }
});

Error Handling Best Practices

Use Error Codes

const ERROR_CODES = {
  VALIDATION_ERROR: { status: 400, message: 'Invalid input' },
  NOT_FOUND: { status: 404, message: 'Resource not found' },
  UNAUTHORIZED: { status: 401, message: 'Authentication required' },
  INTERNAL: { status: 500, message: 'Internal server error' },
};

function sendError(res, code, requestId) {
  const error = ERROR_CODES[code] || ERROR_CODES.INTERNAL;
  res.status(error.status).json({
    error: code,
    message: error.message,
    requestId,
  });
}

Environment-Aware Handler

app.use((err, req, res, next) => {
  console.error(err);

  const response = {
    error: 'Internal server error',
    requestId: req.id,
  };

  // Only include stack in development
  if (process.env.NODE_ENV === 'development') {
    response.stack = err.stack; // ⚠️ Development only
  }

  res.status(500).json(response);
});

Security Impact

VulnerabilityCWEOWASPCVSSImpact
Info Disclosure209A01:20214.3 MediumReconnaissance aid
Error Handling755A01:20213.7 LowInformation leakage

Known False Negatives

Indirect Stack Access

Why: Indirect property access not tracked.

// ❌ NOT DETECTED - indirect access
const key = 'stack';
res.json({ [key]: error[key] });

Mitigation: Use explicit error sanitization functions.

Further Reading

On this page

No Headings