Public mode

No access to your tenant or devices. Every item links to its source. What this means

Sources need attention Customize Public modeDiscuss ChangeIntel on Discord

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

CVSS 3.1 4.8 MEDIUMEUVD CVSS 3.1 4.8 CVSS 3.1 · [email protected] · Secondary 4.8 MEDIUMCVSS_V3 Publisher rating MODERATEFixed by @grpc/grpc-js-xds 1.14.2Fixed by @grpc/grpc-js-xds 1.13.2KEV status unknown

cve-program · CVE-2026-126367

Published

@grpc/grpc-js implements the core functionality of gRPC purely in JavaScript, without a C++ addon. Before 1.13.2, and from 1.14.0 through 1.14.1, the @grpc/grpc-js-xds SafeRegexValueMatcher and PathSafeRegexValueMatcher add start and end anchors without grouping a supplied top-level alternation. An expression intended to match one of several complete values can therefore match a prefix, substring, or suffix. The affected matchers are used for route selection, xDS credential identity matching, and RBAC URL path, principal name, server name, and header rules. An ALLOW policy using an alternation can consequently accept a method, certificate identity, or header value outside the intended set, while a DENY policy becomes broader rather than permissive. This issue is fixed in versions 1.13.2 and 1.14.2.

Read the publisher's advisory

euvd · EUVD-2026-124775

Published

@grpc/grpc-js implements the core functionality of gRPC purely in JavaScript, without a C++ addon. Before 1.13.2, and from 1.14.0 through 1.14.1, the @grpc/grpc-js-xds SafeRegexValueMatcher and PathSafeRegexValueMatcher add start and end anchors without grouping a supplied top-level alternation. An expression intended to match one of several complete values can therefore match a prefix, substring, or suffix. The affected matchers are used for route selection, xDS credential identity matching, and RBAC URL path, principal name, server name, and header rules. An ALLOW policy using an alternation can consequently accept a method, certificate identity, or header value outside the intended set, while a DENY policy becomes broader rather than permissive. This issue is fixed in versions 1.13.2 and 1.14.2.

Read the publisher's advisory

nvd · CVE-2026-126367

Published

@grpc/grpc-js implements the core functionality of gRPC purely in JavaScript, without a C++ addon. Before 1.13.2, and from 1.14.0 through 1.14.1, the @grpc/grpc-js-xds SafeRegexValueMatcher and PathSafeRegexValueMatcher add start and end anchors without grouping a supplied top-level alternation. An expression intended to match one of several complete values can therefore match a prefix, substring, or suffix. The affected matchers are used for route selection, xDS credential identity matching, and RBAC URL path, principal name, server name, and header rules. An ALLOW policy using an alternation can consequently accept a method, certificate identity, or header value outside the intended set, while a DENY policy becomes broader rather than permissive. This issue is fixed in versions 1.13.2 and 1.14.2.

Read the publisher's advisory

osv · GHSA-r7hq-q2xm-6c5m

Published

### 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 ```ts 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: ```js 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.

Read the publisher's advisory

Affected versions, risk and source history

Related advisories and sources

Publishers link these advisories through the identifiers shown below. A shared identifier or date alone does not tell you whether your software is affected or whether the advisories describe the same cause.

CVE-2026-126367 · CVE overview and exploitation status

EUVD-2026-124775

GHSA-r7hq-q2xm-6c5m

cve-program · CVE-2026-126367

euvd · EUVD-2026-124775

nvd · CVE-2026-126367

osv · GHSA-r7hq-q2xm-6c5m

Related changes

Linked by CVE identifier
ChangeIntel

An IT change radar: releases, security, known issues, retirements, documentation changes, and service status from public sources. Every item links to supporting evidence; dates and statuses can change after they are read.

Sources read 15 Nov 18:02 UTC · 254 of 275 readable · 250 fully read · documentation 469/469 current