## Ticket: CVE-2026-25244 — `@wdio/browserstack-service` OS command injection via git branch name

**Advisory**: GHSA-5c46-x3qw-q7j7 — https://github.com/advisories/GHSA-5c46-x3qw-q7j7
**CVE**: CVE-2026-25244 | **CWE-78** (OS Command Injection)
**Severity**: Critical — NVD CVSS 3.1 base 9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)
**Package**: `@wdio/browserstack-service` (npm) | **Repository**: https://github.com/webdriverio/webdriverio
**Type**: OS Command Injection → Remote Code Execution

### Impact

`@wdio/browserstack-service` is the BrowserStack integration for WebdriverIO,
shipped from the `webdriverio` monorepo (package source under
`packages/wdio-browserstack-service/`). To support BrowserStack's AI test
selection feature it collects git metadata about the working directory.

The helper `getGitMetadataForAISelection()` interpolates the **current git
branch name** directly into a shell command string that is passed to
`child_process.execSync()`. The branch name is never sanitized or escaped.

Because a git branch name can legally contain shell metacharacters
(backticks, `$(...)`, `;`, `|`, `&`), a repository whose checked-out branch
embeds a payload causes that payload to be **executed by the shell** the moment
the service runs the helper. This is arbitrary OS command execution in the
context of the process running the WebdriverIO test session — typically a CI
runner — i.e. remote code execution (CWE-78).

This is realistic in CI: a pull-request branch name is attacker-controlled, and
automation that checks out the PR branch and runs the BrowserStack service will
execute whatever the attacker put in the branch name.

### Affected / fixed versions

Affected: `@wdio/browserstack-service` `<= 9.23.2` (webdriverio `< 9.24.0`).
Fixed: **`9.24.0`**.

Reproduce on a vulnerable build (**`9.23.2`**) and verify the fix on
**`9.24.0`**.

### Where to look

The vulnerable sink is `getGitMetadataForAISelection()` in the
`@wdio/browserstack-service` package
(`packages/wdio-browserstack-service/` in the webdriverio monorepo). Inspect the
difference between the vulnerable and fixed releases to confirm the root cause
and the fix:

```bash
git clone https://github.com/webdriverio/webdriverio.git
cd webdriverio
git diff v9.23.2 v9.24.0 -- packages/wdio-browserstack-service/
```

(Equivalently, diff the two published npm tarballs
`@wdio/browserstack-service@9.23.2` and `@wdio/browserstack-service@9.24.0`.)
Confirm: in `9.23.2` the git branch name flows unescaped into an `execSync()`
command string inside `getGitMetadataForAISelection()`; in `9.24.0` the branch
name is sanitized / passed safely so shell metacharacters can no longer be
evaluated.

### Reproduction approach

No live BrowserStack account, network service, or browser is needed — the
command-injection sink is reached locally when the git-metadata helper runs
against a malicious branch name. The observable difference is purely on the
local filesystem.

1. In a scratch directory, install the **vulnerable** build:
   `npm install @wdio/browserstack-service@9.23.2`
2. Create a git repository and check out a branch whose name embeds a shell
   payload, for example a branch name containing ``` `touch /tmp/pwned` ```
   or `;touch /tmp/pwned;` (a marker file in a writable temp path).
3. From inside that repository, invoke the code path that reaches
   `getGitMetadataForAISelection()`. The branch name is interpolated into the
   `execSync()` command string and the shell evaluates the payload.
4. Check whether the marker file (`/tmp/pwned`) now exists.
5. Repeat with the **fixed** build: `npm install @wdio/browserstack-service@9.24.0`,
   using a fresh marker path.

### Expected result

| Build | Branch name with embedded payload | Marker file after running the git-metadata helper |
|-------|-----------------------------------|---------------------------------------------------|
| `@wdio/browserstack-service@9.23.2` (vulnerable) | interpolated unescaped into `execSync()` | **created** — the injected command executed (RCE) |
| `@wdio/browserstack-service@9.24.0` (fixed) | sanitized / passed safely | **not created** — shell metacharacters not evaluated |

- **Vulnerable indicator**: after the helper runs, the marker file exists —
  the command embedded in the branch name was executed by the shell.
- **Fixed indicator**: the marker file does not exist — the branch name was
  neutralized and no injected command ran.

### Expected artifacts

- `reproduction_steps.sh` — sets up the malicious git repo/branch, installs
  both versions, runs the PoC, and prints whether the marker file was created
  for each build.
- `validation_verdict.json` — `verdict: confirmed`, with the vulnerable and
  fixed indicators captured.
- Logs capturing the PoC output for `9.23.2` and `9.24.0`.
