Jun 3, 2026·7 min read·26 visits
BrowserStack Runner through 0.9.5 permits unauthenticated remote file disclosure due to lack of path sanitization in its internal HTTP server handlers.
An unauthenticated path traversal vulnerability in BrowserStack Runner versions up to and including 0.9.5 allows remote or adjacent network attackers to read arbitrary files from the host system. The flaw exists within the local HTTP test server's fallback and patch file handlers, which fail to sanitize path inputs before passing them to file resolution APIs.
The BrowserStack Runner utility is an integration tool developed to automate JavaScript-based unit tests across a range of remote web browsers. During test runs, the utility initializes a local HTTP server using the Node.js native http module. This server acts as the central mechanism for hosting and delivering JavaScript test suites, static testing assets, and framework-specific patch files to both the local host and remote BrowserStack worker instances.
The default binding configuration of this HTTP server exposes it to any reachable network interface, resolving to 0.0.0.0. While this configuration simplifies communication with remote cloud-based browsers, it opens an unauthenticated listening port on the system execution context. Any host on the local or adjacent network can communicate with this service, establishing a direct attack surface.
A validation failure exists within the component responsible for routing incoming HTTP requests. The routing mechanism fails to perform directory containment verification on user-controlled URI paths before handling file reads. This flaw corresponds to CWE-22 (Improper Limitation of a Pathname to a Restricted Directory) and permits an unauthenticated threat actor to bypass directory boundaries and read arbitrary files residing on the target system.
The root cause of CVE-2026-49144 is located within lib/server.js. The local HTTP server relies on a manual routing implementation to classify and process incoming client requests. The server extracts the raw request pathname into the uri variable, then attempts to determine the target handler by splitting the pathname at slash separators and selecting the first subdirectory segment as the routing key (method = uri.split('/')[1]).
If an incoming URL pathname does not begin with one of the predefined routing paths (such as _progress, _report, or _log), the router evaluates the key as undefined and defaults the processing task to handlers._default. Because the router passes the original, unmodified, and unvalidated uri parameter directly to the fallback handler, an attacker can supply directory traversal sequences like ../ to alter the target path.
Inside the _default handler, the path resolution is performed via path.join(process.cwd(), uri). Node.js's native path.join helper normalizes the directory traversal sequences, resulting in an absolute file path that escapes the current working directory boundary. A secondary vector exists in the _patch handler, which behaves similarly by resolving paths relative to the package installation folder (__dirname) via path.join(__dirname, uri) without validation.
The vulnerability is compounded by the behavior of the internal file-serving mechanism handleFile. For non-HTML files, handleFile executes the third-party send library to stream the file contents to the client. While the send package features native directory traversal protection, this security boundary is only activated when a root path is configured in its options. Because handleFile invokes the function without defining a root directory, the utility processes arbitrary absolute file paths directly, executing the disk read without constraint.
To illustrate the vulnerability, analyze the vulnerable implementation of the router fallback and path resolution logic in lib/server.js:
// Vulnerable Routing and Fallback Implementation
return http.createServer(function(request, response) {
var uri = url.parse(request.url).pathname;
var method = uri.split('/')[1];
var body = '';
request.on('data', function(data) {
body += data;
});
request.on('end', function() {
// Unvalidated fallback allows traversal sequences in 'uri'
(handlers[method] || handlers._default)(uri, body, request, response);
});
});The following segment exhibits the vulnerable _default and _patch handlers, where unchecked concatenations allow boundary escapes:
// Vulnerable Handlers
var handlers = {
'_patch': function patchHandler(uri, body, request, response) {
// Concatenating unchecked uri to __dirname
var filePath = path.join(__dirname, uri);
logger.trace('_patch', filePath);
handleFile(filePath, request, response, true);
},
'_default': function defaultHandler(uri, body, request, response) {
// Concatenating unchecked uri to current working directory
var filePath = path.join(process.cwd(), uri);
logger.trace('_default', filePath);
handleFile(filePath, request, response);
}
};Remediation requires implementing strict boundary containment checks using path.resolve and verification that the resolved absolute path starts with the base directory path. The following updated source code illustrates the complete patch:
// Safe Path Verification Helper
function isSafePath(baseDir, targetPath) {
var resolvedBase = path.resolve(baseDir);
var resolvedTarget = path.resolve(targetPath);
// Enforce that the target path remains nested within the base directory
return resolvedTarget.startsWith(resolvedBase);
}
var handlers = {
'_patch': function patchHandler(uri, body, request, response) {
var filePath = path.join(__dirname, uri);
if (!isSafePath(__dirname, filePath)) {
sendError(response, 'Forbidden', 403);
return;
}
logger.trace('_patch', filePath);
handleFile(filePath, request, response, true);
},
'_default': function defaultHandler(uri, body, request, response) {
var filePath = path.join(process.cwd(), uri);
if (!isSafePath(process.cwd(), filePath)) {
sendError(response, 'Forbidden', 403);
return;
}
logger.trace('_default', filePath);
handleFile(filePath, request, response);
}
};Exploitation of CVE-2026-49144 does not require authentication or specific environment state, except that the BrowserStack Runner test server must be actively running and accessible on an reachable network interface. The attacker targets the local port, which defaults to 3000 or can be identified via active network scanning.
Because typical HTTP clients such as web browsers or standard curl utilities automatically normalize path segments locally before transmission, direct exploitation attempts may fail if the client strips the traversal sequences (../). The attacker must transmit the raw, un-normalized dot-dot-slash sequence in the request line. Using curl with the --path-as-is command-line option preserves the exact traversal sequences.
The attack sequence diagram describes the payload flow through the system:
Alternatively, the attacker can leverage the _patch routing handler to bypass checks by prepending the route name to the traversal sequence. The payload GET /_patch/../../../../../../etc/passwd resolves to handlers._patch, escapes __dirname, and exposes /etc/passwd.
The impact of this vulnerability is high confidentiality loss. An unauthenticated network-adjacent or local attacker can retrieve arbitrary files from the system running the BrowserStack Runner process, subject to the file permissions of the user context executing the tests.
Typical targets for data exfiltration in development or continuous integration (CI) environments include SSH private keys, cloud service provider credentials, application source code, API tokens, and local system configuration files. If the runner is executed within a privileged continuous integration pipeline (such as a GitHub runner, GitLab runner, or Jenkins agent), the credentials retrieved could allow the attacker to compromise code repositories or cloud infrastructure.
The vulnerability is tracked with a CVSS v4.0 base score of 7.1, with the vector emphasizing an adjacent network attack path, low complexity, and high confidentiality impact on the affected system. Because BrowserStack Runner is a local development utility and does not perform modifying actions or manage resources, there is zero impact on system integrity or availability.
The primary mitigation is the application of the path validation check within the lib/server.js file of local installations. If updating the codebase is not immediately possible, security teams must enforce localized binding rules. Modifying the test configuration to bind the local server strictly to the loopback interface (127.0.0.1 or localhost) prevents remote or network-adjacent hosts from accessing the open port.
In addition, local host-based firewalls (such as Windows Defender Firewall, iptables, or ufw) should be configured to drop incoming packets targeting the runner's test ports from external network zones. Host-based intrusion detection systems or web application firewalls can apply pattern matching signatures to block requests containing directory traversal sequences.
Continuous integration agents and development workstations should avoid running test suites with administrative privileges. Restricting the process owner account limits the filesystem exposure to the runner's working directory and prevents the exposure of critical administrative configuration assets like /etc/shadow.
CVSS:4.0/AV:A/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
BrowserStack Runner BrowserStack | <= 0.9.5 | None |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-22 |
| Attack Vector | Adjacent Network (AV:A) |
| CVSS v4 Score | 7.1 (High) |
| EPSS Score | 0.00024 |
| Impact | Arbitrary File Disclosure |
| Exploit Status | PoC |
| KEV Status | Not Listed |
The software uses external input to construct a pathname that should be within a restricted directory, but it does not properly neutralize security-sensitive elements such as directory traversal sequences.
A stored Cross-Site Scripting (XSS) vulnerability exists within plone.restapi, the REST API package for Plone content management system. By supplying a spoofed input MIME type (text/x-html-safe), an attacker can mislead the rendering layer (plone.app.textfield) into assuming that the supplied content is already sanitized. This causes the system to skip the safe_html transform, allowing arbitrary JavaScript to execute in the victim's browser when they view the compromised page.
An untrusted search path vulnerability in the GlobalDatabasePlugin component of the AWS Advanced JDBC Wrapper for Amazon Aurora PostgreSQL allows authenticated, low-privilege database users to hijack administrative session queries. By defining a custom function in a writable schema such as the public schema, an attacker can hijack queries executed automatically during driver-level topology detection. When a highly privileged database user connects to the database utilizing an affected version of the wrapper, the custom function executes under their security context, enabling remote privilege escalation to rds_superuser.
CVE-2026-27771 represents a critical security flaw in Gitea and Forgejo (up to and including version 1.26.1) involving missing authorization checks (CWE-862). Unauthenticated remote attackers can query, enumerate, and download private container images from the OCI-compliant container registry. Additionally, unauthorized users can retrieve private or internal source repository URLs via the Composer package registry metadata API. A public proof-of-concept exists, and threat metrics indicate highly active scanning and exploitation risks.
A missing authorization vulnerability in the Formie plugin for Craft CMS prior to version 3.1.28 allows low-privileged Control Panel users to read and modify sensitive administrative settings, configuration options, and third-party integrations.
CVE-2026-53598 is a directory traversal and arbitrary file read vulnerability in Microsoft Prompty ecosystem loaders across multiple languages. Prior to version 2.0.0-beta.2, the loaders resolved `${file:...}` reference strings inside frontmatter configuration blocks without enforcing that the target file paths resided within authorized directories. This deficiency allows an attacker-controlled configuration file to read sensitive operating system and application files through absolute paths, directory traversal, or symbolic link escapes. The issue is addressed across the Python, C#, Node.js/TypeScript, and Rust ecosystems.
A directory traversal vulnerability exists in the copy subcommand of the proot-distro utility. Due to incomplete path sanitization, local attackers or malicious scripts can read from or write to arbitrary files outside the container rootfs, bypassing isolation barriers and potentially gaining unauthorized access or persistent execution on the host system.