Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2026-48049

CVE-2026-48049: @hapi/inert Path Traversal Vulnerability

CVE-2026-48049 is a path traversal vulnerability in @hapi/inert that allows attackers to read files outside the intended directory. This post covers technical details, affected versions, security impact, and mitigation.

Published:

CVE-2026-48049 Overview

CVE-2026-48049 is a path traversal vulnerability [CWE-22] in @hapi/inert, the static file and directory handler for the hapi.js web framework. Versions from 4.0.0 through 7.1.0 contain a flawed confinement check that compares resolved absolute paths against the confine directory using a raw string-prefix test. An unauthenticated remote attacker can craft a URL such as /..%2fstatic-secret/secret.txt to read files from sibling directories that share a string prefix with the configured confine path. The issue is fixed in version 7.1.1.

Critical Impact

Unauthenticated remote attackers can read files from sibling directories outside the intended static file scope, exposing configuration files, credentials, or source code adjacent to the configured static directory.

Affected Products

  • @hapi/inert versions 4.0.0 through 7.1.0
  • hapi.js applications using directory or file handlers with the confine option
  • hapi.js applications using h.file() with relativeTo

Discovery Timeline

  • 2026-07-17 - CVE-2026-48049 published to NVD
  • 2026-07-23 - Last updated in NVD database

Technical Details for CVE-2026-48049

Vulnerability Analysis

@hapi/inert serves static files from a directory configured through the path option in directory or file handlers, or through relativeTo for h.file(). The confine option is intended to restrict served files to a specific directory tree. The confinement check resolved the requested path to an absolute path, then verified it began with the confine directory string. Because the check used a raw string-prefix comparison, any sibling directory whose name shared the confine directory as a prefix was incorrectly accepted as being inside it.

For a confine directory of /app/static, a resolved path such as /app/static-secret/secret.txt passes the prefix test even though static-secret is a sibling directory, not a child of static. Attackers exploit this using URL-encoded traversal sequences like /..%2fstatic-secret/secret.txt to escape the confinement boundary.

Root Cause

The root cause is an incorrect string-prefix comparison in lib/file.js that failed to enforce a directory separator between the confine directory and the requested subpath. The check path.lastIndexOf(confineDir, 0) !== 0 treats any string beginning with the confine directory characters as valid, ignoring that filesystem containment requires either equality or a path separator boundary.

Attack Vector

The attack requires no authentication and no user interaction. An attacker sends a crafted HTTP request containing URL-encoded traversal sequences that resolve to a sibling directory sharing the confine directory prefix. The server returns the file contents if the sibling path exists and is readable.

javascript
         const confineDir = Path.resolve(request.route.settings.files.relativeTo, options.confine);
         path = Path.isAbsolute(path) ? Path.normalize(path) : Path.join(confineDir, path);
 
-        // Verify that resolved path is within confineDir
-        if (path.lastIndexOf(confineDir, 0) !== 0) {
+        // Verify that the resolved path is the confine dir itself or a path
+        // inside it. Comparing against `confineDir + Path.sep` prevents a sibling
+        // directory with the same string prefix (e.g. confineDir + '-secret')
+        // from being treated as confined.
+        if (path !== confineDir &&
+            !path.startsWith(confineDir + Path.sep)) {
+
             path = null;
         }
     }

Source: GitHub Commit bcceb76. The patch replaces the raw prefix test with a comparison that requires either path equality with confineDir or a match against confineDir + Path.sep, ensuring sibling directories are rejected.

Detection Methods for CVE-2026-48049

Indicators of Compromise

  • HTTP request URIs containing URL-encoded traversal sequences such as ..%2f, ..%5c, or %2e%2e/ targeting static file routes.
  • Access log entries showing 200 OK responses for paths resolving outside the intended static asset directory tree.
  • Requests to sibling directories that share a name prefix with the configured static path, for example static-secret, static-backup, or static.old.

Detection Strategies

  • Parse web server and reverse proxy logs for decoded request paths and flag any resolved path that leaves the expected static asset root.
  • Inspect application dependency manifests (package.json, package-lock.json, yarn.lock) for @hapi/inert versions between 4.0.0 and 7.1.0.
  • Deploy web application firewall rules that decode percent-encoded sequences before matching against traversal patterns to prevent evasion.

Monitoring Recommendations

  • Alert on repeated 4xx or 2xx responses to routes served by @hapi/inert handlers that contain traversal payloads.
  • Monitor filesystem access telemetry on hapi.js hosts for reads of sensitive files from directories adjacent to configured static roots.
  • Track outbound response sizes on static handlers to identify anomalous exfiltration of files not intended for public access.

How to Mitigate CVE-2026-48049

Immediate Actions Required

  • Upgrade @hapi/inert to version 7.1.1 or later across all hapi.js applications.
  • Audit filesystem layout for sibling directories that share a name prefix with any configured confine or relativeTo path and relocate sensitive data.
  • Review access logs for the affected time window to identify prior exploitation attempts targeting sibling directories.

Patch Information

The fix is available in @hapi/inert version 7.1.1. Details are published in the GitHub Security Advisory GHSA-rcvq-m9j9-6f4g, the GitHub Release v7.1.1 notes, and the Pull Request discussion.

Workarounds

  • Restructure filesystem layout so that no directory adjacent to a served static root shares a name prefix with it, eliminating the sibling condition required for exploitation.
  • Place a reverse proxy or web application firewall in front of hapi.js that rejects requests containing encoded traversal sequences before they reach the application.
  • Restrict filesystem permissions on the process running hapi.js so that sensitive sibling files are unreadable by the service account.
bash
# Upgrade @hapi/inert to the patched release
npm install @hapi/inert@^7.1.1

# Verify the installed version
npm ls @hapi/inert

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.