Back to Blog
critical SEVERITY7 min read

How Path Traversal happens in JavaScript i18n loaders and how to fix it

A path traversal vulnerability in `beta/js/i18n-chatrd.js` allowed attackers to manipulate the `lang` URL query parameter to load arbitrary JSON files from the web server by injecting payloads like `../../sensitive-file`. The fix adds input validation to ensure only safe, expected language codes are accepted before they are interpolated into the fetch URL. This type of vulnerability is especially dangerous in internationalization loaders because they are often publicly accessible and designed to

O
By Orbis AppSec
Published August 26, 2026Reviewed August 26, 2026

Answer Summary

This is a path traversal vulnerability (CWE-22) in JavaScript, specifically in the `beta/js/i18n-chatrd.js` i18n loader. The `lang` parameter is read directly from the URL query string and interpolated into a `fetch()` call without validation, allowing attackers to supply payloads like `../../sensitive-file` to retrieve arbitrary JSON files from the server. The fix adds a validation step that sanitizes or allowlists the `lang` parameter before it is used in the fetch URL, preventing traversal sequences from reaching the filesystem.

Vulnerability at a Glance

cweCWE-22
fixValidate the `lang` parameter against an allowlist or strip path traversal sequences before constructing the fetch URL
riskAttackers can read arbitrary files accessible to the web server by manipulating the `lang` URL parameter
languageJavaScript
root causeThe `lang` query string parameter is interpolated directly into a `fetch()` URL without validation or sanitization
vulnerabilityPath Traversal

How Path Traversal Happens in JavaScript i18n Loaders and How to Fix It


Vulnerability at a Glance

Field Detail
Vulnerability Path Traversal
CWE CWE-22
Language JavaScript
Risk Arbitrary file read via manipulated lang URL parameter
Root Cause Unvalidated query parameter interpolated into fetch() URL
Fix Validate lang against an allowlist before constructing the fetch URL

Introduction

The beta/js/i18n-chatrd.js file is responsible for loading internationalization (i18n) language files dynamically — a common and useful pattern in web applications that support multiple locales. The file reads the lang parameter from the URL query string and uses it to fetch the appropriate translation JSON. This is exactly the kind of code that feels harmless at first glance: it's just loading a language file, right?

The problem is that nothing in the original code checked whether lang actually looked like a language code. An attacker who noticed this behavior could replace a benign value like en or fr with a path traversal payload like ../../config/secrets, potentially causing the application to fetch and expose files it was never meant to serve.

This vulnerability was assigned CWE-22: Improper Limitation of a Pathname to a Restricted Directory and rated HIGH severity — a justified rating given how easily it can be triggered with a crafted URL.


The Vulnerability Explained

What the Code Was Doing

The vulnerable pattern in beta/js/i18n-chatrd.js follows a structure like this:

// Vulnerable code (before fix)
const params = new URLSearchParams(window.location.search);
const lang = params.get('lang');

fetch(`/locales/${lang}.json`)
  .then(res => res.json())
  .then(data => { /* load translations */ });

The lang variable is read directly from the URL query string using URLSearchParams and then interpolated into the fetch() URL path without any validation. There is no check that lang is a valid locale identifier, no stripping of special characters, and no allowlist of accepted values.

How an Attacker Exploits This

Because the lang value flows directly into the fetch URL, an attacker can craft a request like:

https://example.com/chat?lang=../../sensitive-file

This causes the browser to fetch:

/locales/../../sensitive-file.json

Which resolves on the server to:

/sensitive-file.json

Depending on the server configuration and what files are accessible at the web root, this could expose:

  • Configuration files containing API keys or database credentials
  • Other JSON files with internal application data
  • Any file the web server has permission to read and serve

The attack requires no authentication, no special tooling, and no prior knowledge beyond the existence of the lang parameter — all of which are easily discoverable by inspecting the page source or network requests.

Why i18n Loaders Are a Common Target

Internationalization loaders are particularly attractive targets for this class of vulnerability because:

  1. They are designed to fetch files dynamically — the file-fetching behavior is intentional, making it easy to overlook the security implications.
  2. They are publicly accessible — language selection is typically available to all users, including unauthenticated ones.
  3. The parameter name is predictablelang, locale, language are standard names that attackers actively probe.

The Fix

The fix adds path validation to the lang parameter before it is used in the fetch() call. The core principle is simple: never trust user input when constructing file paths or URLs.

Before and After

Before (vulnerable):

const params = new URLSearchParams(window.location.search);
const lang = params.get('lang');

// lang is used directly — no validation
fetch(`/locales/${lang}.json`)
  .then(res => res.json())
  .then(data => loadTranslations(data));

After (fixed):

const params = new URLSearchParams(window.location.search);
const lang = params.get('lang');

// Validate lang against an allowlist of known locale codes
const ALLOWED_LANGS = ['en', 'fr', 'de', 'es', 'ja', 'zh'];
const safeLang = ALLOWED_LANGS.includes(lang) ? lang : 'en';

fetch(`/locales/${safeLang}.json`)
  .then(res => res.json())
  .then(data => loadTranslations(data));

Why This Fix Works

By checking lang against an explicit allowlist of valid locale codes before constructing the URL, the fix ensures that:

  • Path traversal sequences like ../ never reach the fetch URL.
  • Unexpected values silently fall back to the default language (en), preserving functionality.
  • The attack surface is eliminated — even a perfectly crafted traversal payload is rejected at the validation step.

An alternative approach that is also effective when a static allowlist is impractical is to validate the format of the lang parameter using a strict regular expression:

// Alternative: regex-based validation for BCP 47 language tags
const langPattern = /^[a-zA-Z]{2,3}(-[a-zA-Z]{2,4})?$/;
const safeLang = langPattern.test(lang) ? lang : 'en';

This approach ensures that only strings matching the pattern of a real language code (e.g., en, en-US, zh-CN) are accepted, blocking any payload containing /, ., or other traversal characters.


Prevention & Best Practices

1. Always Validate External Input Before Using It in Paths or URLs

Any value that originates from user input — URL parameters, form fields, cookies, headers — must be validated before being used to construct file paths, URLs, or database queries. This is the foundational rule of secure input handling.

2. Prefer Allowlists Over Denylists

It is tempting to try to block known-bad patterns like ../ or %2e%2e. However, attackers have a large repertoire of encoding tricks to bypass denylist filters:

  • ../%2e%2e%2f
  • ../..%2f
  • ../%2e%2e/

An allowlist of valid values (or a strict format validation) is far more robust because it only permits what you explicitly expect.

3. Use path.basename() as a Defense-in-Depth Measure (Node.js)

If you are constructing file paths on the server side in Node.js, path.basename() strips all directory components from a path, leaving only the filename:

const path = require('path');
const safeFile = path.basename(userInput); // '../../etc/passwd' → 'passwd'

This is not a complete fix on its own but is a useful defense-in-depth layer.

4. Apply the Same Validation to Similar Parameters

In any codebase with i18n loaders, check for similar patterns in other files. Parameters named locale, language, theme, template, page, or file are all common candidates for path traversal vulnerabilities.

5. Security Standards and References


Key Takeaways

  • The lang parameter in i18n-chatrd.js was the entry point — a URL query string value that was never validated before being used in a fetch() call.
  • Path traversal in client-side fetch calls is just as dangerous as server-side file reads — the server still resolves the path and serves whatever it finds.
  • Allowlisting locale codes is the correct fix for this specific pattern, because the set of valid language codes is finite and well-known.
  • i18n loaders are a non-obvious attack surface — their dynamic file-loading behavior makes them easy to overlook during security reviews.
  • A regex or allowlist check adds negligible overhead but completely eliminates the traversal risk in this code path.

How Orbis AppSec Detected This

  • Source: The lang parameter read from window.location.search via URLSearchParams.get('lang') in beta/js/i18n-chatrd.js
  • Sink: The unvalidated lang value interpolated directly into a fetch() URL string, e.g., fetch(`/locales/${lang}.json`)
  • Missing control: No allowlist validation, format check, or sanitization was applied to lang before it was used in the URL path
  • CWE: CWE-22 — Improper Limitation of a Pathname to a Restricted Directory
  • Fix: A validation step was added to check lang against an allowlist of permitted locale codes before constructing the fetch URL, with a safe default fallback

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.


Conclusion

The path traversal vulnerability in beta/js/i18n-chatrd.js is a clear example of how a small oversight — using a URL parameter without validation — can open a significant security hole. The lang parameter seemed innocuous because its intended use is benign, but the absence of any input validation meant that an attacker could redirect the fetch() call to any JSON file accessible on the server.

The fix is straightforward and the lesson is broadly applicable: every piece of user-supplied input that influences a file path or URL must be validated before use. Allowlisting expected values is the most reliable approach, and for i18n loaders specifically, the set of valid locale codes is always finite and easy to enumerate.

Security vulnerabilities in i18n and localization code are easy to miss because they don't look like traditional attack surfaces. Regular static analysis, code review with a security lens, and automated tools like Orbis AppSec are essential for catching these issues before they reach production.


References

Frequently Asked Questions

What is path traversal?

Path traversal (CWE-22) is a vulnerability where user-supplied input is used to construct a file path without proper validation, allowing attackers to navigate outside the intended directory and access arbitrary files.

How do you prevent path traversal in JavaScript?

Validate user input against an allowlist of expected values, use `path.basename()` to strip directory components, or encode and sanitize inputs before using them in file paths or fetch URLs.

What CWE is path traversal?

Path traversal is classified as CWE-22: Improper Limitation of a Pathname to a Restricted Directory.

Is URL encoding enough to prevent path traversal?

No. Attackers can use encoded variants like `%2e%2e%2f` or double-encoded sequences to bypass simple encoding checks. An allowlist of valid values is the most reliable defense.

Can static analysis detect path traversal?

Yes. Static analysis tools like Semgrep, CodeQL, and Orbis AppSec can trace tainted data from sources like `URLSearchParams` to dangerous sinks like `fetch()` and flag unvalidated interpolation.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #9

Related Articles

high

How Path Traversal happens in Node.js PostCSS and how to fix it

A high-severity path traversal vulnerability in PostCSS versions before 8.5.18 allowed attackers to exploit the `sourceMappingURL` auto-loading mechanism to read arbitrary `.map` files from the filesystem. The fix upgrades PostCSS from 8.5.8 to 8.5.18 and pins the dependency via an npm `overrides` entry, closing the attack surface entirely. Any project using PostCSS as a direct or transitive dependency should apply this upgrade immediately.

high

How Path Traversal happens in Python FastAPI and how to fix it

A critical path traversal vulnerability was discovered in `SovitsTest/GSVI.py`, a FastAPI-based TTS inference server, where the `/upload` endpoint accepted user-supplied filenames without sanitization. An unauthenticated remote attacker could exploit this to write arbitrary files anywhere on the filesystem — including sensitive system directories like `/etc/cron.d`. The fix adds path validation to prevent filenames from escaping the intended upload directory.

high

How Path Traversal happens in Python Flask routes and how to fix it

A high-severity path traversal vulnerability was discovered in `xkeen-ui/routes/cores_status.py` at line 221, where user-controlled input was passed directly to Python's `open()` function without sanitization. An attacker could exploit this to read arbitrary files on the server by supplying crafted path strings like `../../etc/passwd`. The fix introduces strict path validation using a trusted root directory, ensuring only files within the intended directory can be accessed.

critical

How Path Traversal happens in Vitest UI Server and how to fix it

CVE-2026-47429 is a critical path traversal vulnerability in Vitest's UI server that allows unauthenticated attackers to read and execute arbitrary files on the host system when the UI server is active. The vulnerability was fixed by upgrading Vitest from the vulnerable `^4.0.0` range to the pinned safe release `4.1.0`. Any project running Vitest's UI mode during development or CI is potentially exposed until this upgrade is applied.

critical

How Local File Inclusion/Path Traversal happens in JavaScript PDF generation and how to fix it

CVE-2025-68428 is a critical Local File Inclusion/Path Traversal vulnerability in jsPDF versions prior to 4.0.0 that could allow attackers to read arbitrary files from the server's filesystem through unsanitized path inputs during PDF generation. The vulnerability was present in the `jspdf` dependency declared in `frontend/package-lock.json`, and was resolved by upgrading from version 3.0.4 to 4.0.0. Left unpatched, this flaw could expose sensitive server-side files to unauthorized access via cr

critical

How eval() Code Injection happens in JavaScript and how to fix it

A critical code injection vulnerability was discovered in `js/lib/jsencrypt.js` at line 195, where a direct `eval()` call executed a JavaScript string shim for the `process` object in browser environments. If an attacker could influence the string passed to `eval()`—through a compromised dependency, a man-in-the-middle attack, or supply chain tampering—they could achieve arbitrary JavaScript execution in any user's browser. The fix replaces the `eval()` call with the equivalent inline JavaScript