HIGH path traversalhapi

Path Traversal in Hapi

How Path Traversal Manifests in Hapi

Path traversal vulnerabilities in Hapi applications typically arise from improper handling of file system operations where user-controlled input determines file paths. Hapi's routing system and handler patterns create specific scenarios where these vulnerabilities become exploitable.

The most common pattern involves using request.params or request.query values directly in file system operations without proper validation. Consider a file download endpoint:

server.route({
  method: 'GET',
  path: '/download/{filename}',
  handler: (request, h) => {
    const filePath = `/var/files/${request.params.filename}`;
    return h.file(filePath);
  }
});

An attacker can request /download/../../../etc/passwd to traverse outside the intended directory. Hapi's path parameter parsing doesn't inherently prevent these sequences.

Another Hapi-specific pattern involves dynamic view rendering with user input:

server.views({
  engines: { html: require('handlebars') },
  path: './templates'
});

If a handler uses user input to determine which template to render:

const templateName = request.query.template;
server.render(templateName, context);

An attacker could request ?template=../../package.json to read arbitrary files if the view engine allows it.

Static file serving with dynamic paths presents another attack surface:

server.route({
  method: 'GET',
  path: '/static/{path*}',
  handler: {
    directory: {
      path: './public',
      listing: true
    }
  }
});

The wildcard path parameter {path*} can be exploited with sequences like /static/../../package.json to access files outside the public directory.

Hapi's plugin system can introduce path traversal through plugins that handle file uploads or dynamic content generation. A plugin that saves uploaded files using user-provided names without sanitization creates an obvious vulnerability:

const upload = require('hapi-upload');
server.register({ plugin: upload });
server.route({
  method: 'POST',
  path: '/upload',
  handler: (request, h) => {
    const file = request.payload.file;
    const filename = file.hapi.filename; // User-controlled
    const savePath = `/uploads/${filename}`;
    // Save file without validation
    return { success: true };
  }
});

The h.file() response toolkit method itself doesn't validate paths, making it dangerous when combined with user input. Hapi's architecture allows these patterns to emerge naturally when developers prioritize convenience over security.

Hapi-Specific Detection

Detecting path traversal in Hapi applications requires examining both the routing configuration and handler implementations. Static analysis can identify risky patterns, but runtime scanning provides comprehensive coverage.

Code review should focus on these Hapi-specific patterns:

// Dangerous: direct parameter usage
const filePath = `/uploads/${request.params.filename}`;
// Safer: validate before use
const filename = request.params.filename;
if (!/^[a-zA-Z0-9_.-]+$/.test(filename)) {
  return h.response('Invalid filename').code(400);
}
const filePath = `/uploads/${filename}`;

Scan for wildcard path parameters {path*} that allow arbitrary path segments. These parameters can match multiple path segments, making traversal attacks more flexible.

middleBrick's scanning approach for Hapi applications includes:

  • Endpoint discovery through Hapi's routing table (if accessible)
  • Parameter fuzzing with traversal payloads like ../, ..∖, and URL-encoded variants
  • Response analysis for directory listing indicators or file content leaks
  • Content-type checking to distinguish between intended responses and unintended file disclosures

The scanner tests common traversal sequences:

../etc/passwd
..\windows\system32\drivers\etc\hosts
./././boot.ini
%2e%2e%2fetc%2fpasswd
%2e%2e%5cwindows%5c%2f

For Hapi applications using plugins, middleBrick analyzes plugin configurations that might introduce file handling capabilities. The scanner checks for:

  • Directory listing enabled on file routes
  • Static file serving with broad path patterns
  • Upload handlers with user-controlled filenames
  • Template rendering with dynamic template names

Runtime detection benefits from middleBrick's black-box scanning methodology. Since no credentials or source code access is required, the scanner can identify vulnerabilities in production APIs immediately. The 5-15 second scan time means you can test multiple endpoints rapidly without service disruption.

middleBrick's LLM security checks are particularly relevant for Hapi applications using AI features. If your Hapi server exposes AI endpoints, the scanner tests for prompt injection and system prompt leakage that could be combined with path traversal for more sophisticated attacks.

Hapi-Specific Remediation

Remediating path traversal in Hapi requires a defense-in-depth approach combining input validation, path normalization, and secure defaults. Hapi provides several native mechanisms to prevent these vulnerabilities.

The most effective approach is using Hapi's built-in validation with Joi schemas:

const Joi = require('@hapi/joi');
server.route({
  method: 'GET',
  path: '/download/{filename}',
  options: {
    validate: {
      params: Joi.object({
        filename: Joi.string()
          .regex(/^[a-zA-Z0-9_.-]+$/) // Alphanumeric, dots, underscores, hyphens
          .max(255)
      })
    }
  },
  handler: (request, h) => {
    const filePath = `/var/files/${request.params.filename}`;
    return h.file(filePath);
  }
});

This validation rejects traversal attempts before they reach the handler logic. The regex pattern ensures only safe characters are accepted.

For dynamic paths, use Hapi's path normalization utilities:

const Path = require('path');
server.route({
  method: 'GET',
  path: '/static/{path*}',
  handler: (request, h) => {
    const userPath = request.params.path.join('/') || 'index.html';
    const safePath = Path.normalize(userPath);
    const fullPath = Path.join(__dirname, 'public', safePath);
    
    // Verify the resolved path is within the intended directory
    if (!fullPath.startsWith(Path.join(__dirname, 'public'))) {
      return h.response('Forbidden').code(403);
    }
    
    return h.file(fullPath);
  }
});

The Path.normalize() call resolves ../ sequences, and the prefix check ensures the final path stays within the public directory.

For file upload handling, sanitize filenames systematically:

const sanitize = require('sanitize-filename');
server.route({
  method: 'POST',
  path: '/upload',
  handler: async (request, h) => {
    const file = request.payload.file;
    const originalName = file.hapi.filename;
    
    // Sanitize filename and generate safe version
    const safeName = sanitize(originalName);
    const timestamp = Date.now();
    const finalName = `${timestamp}_${safeName}`;
    
    const savePath = Path.join(__dirname, 'uploads', finalName);
    
    // Save file securely
    await file.pipe(fs.createWriteStream(savePath));
    
    return { success: true, filename: finalName };
  }
});

The sanitize-filename package removes dangerous characters and path separators, while the timestamp prefix prevents name collisions and obscures original filenames.

When using h.file(), always construct paths with Path.join() and validate the result:

handler: (request, h) => {
  const filename = request.params.filename;
  const safeFilename = sanitize(filename);
  const filePath = Path.join(__dirname, 'files', safeFilename);
  
  // Double-check path is within bounds
  if (!filePath.startsWith(Path.join(__dirname, 'files'))) {
    return h.response('Invalid path').code(400);
  }
  
  return h.file(filePath);
}

For template rendering, use a whitelist approach:

const validTemplates = ['home', 'about', 'contact', 'profile'];
server.route({
  method: 'GET',
  path: '/page/{template}',
  handler: (request, h) => {
    const template = request.params.template;
    
    if (!validTemplates.includes(template)) {
      return h.response('Template not found').code(404);
    }
    
    return h.view(template, { /* context */ });
  }
});

This approach ensures only pre-approved templates can be rendered, eliminating the traversal vector entirely.

Related CWEs: inputValidation

CWE IDNameSeverity
CWE-20Improper Input Validation HIGH
CWE-22Path Traversal HIGH
CWE-74Injection CRITICAL
CWE-77Command Injection CRITICAL
CWE-78OS Command Injection CRITICAL
CWE-79Cross-site Scripting (XSS) HIGH
CWE-89SQL Injection CRITICAL
CWE-90LDAP Injection HIGH
CWE-91XML Injection HIGH
CWE-94Code Injection CRITICAL

Frequently Asked Questions

How does middleBrick scan for path traversal in Hapi applications?
middleBrick performs black-box scanning by sending traversal payloads to Hapi endpoints and analyzing responses. It tests common patterns like ../ sequences, URL-encoded variants, and Windows-specific traversal attempts. The scanner examines response content types and payloads to detect when files are being read outside intended directories. Since no credentials or source code access is needed, middleBrick can scan production Hapi APIs in 5-15 seconds and identify vulnerabilities immediately.
Can path traversal vulnerabilities in Hapi lead to remote code execution?