Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
📦
📦 npm
Not in CISA KEV
MEDIUM severity

GHSA-w4mq-xh27-6xpx

MEDIUMFix: Unleash/unleash@002012c

GHSA-w4mq-xh27-6xpx is a medium-severity (CVSS 4.1) CWE-116 vulnerability in unleash-server. O3 Security confirms whether GHSA-w4mq-xh27-6xpx is actually reachable in your code before you act, and blocks exploitation at runtime until you patch.

Unleash: Global Mustache.escape override disables HTML escaping process-wide, enabling Slack/Teams link-injection via unrestricted username

Also known asCVE-2026-63466
Published
Aug 21, 2026
Updated
Aug 21, 2026
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Aug 21, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

No confirmed exploitation observed yet

  • CISA’s own triage has not observed active exploitation or public proof-of-concept code for this CVE as of its last assessment.

Exploitation and automatability from CISA’s SSVC triage for GHSA-w4mq-xh27-6xpx.

Real-World Exposure

1 pkg affected

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.

2other npm packages depend on this — each one inherits the vulnerability until it's patched upstream
unleash-servernpm
19Kdownloads / week

Description

Vulnerability Details

File: src/lib/addons/feature-event-formatter-md.ts Line: 355 (in v8.0.1; format() method)

Root Cause

FeatureEventFormatterMd.format() does:

Mustache.escape = (text) => text;
const text = Mustache.render(action, context);

mustache (pinned ^4.2.0, confirmed installed 4.2.0) keeps escape as a module-level singleton (mustache.js: mustache.escape = escapeHtml;), read by every Mustache.render() call in the process unless a per-call config.escape override is passed (var escape = this.getConfigEscape(config) || mustache.escape;). Node's module cache guarantees every import Mustache from 'mustache' in the process — feature-event-formatter-md.ts, email-service.ts, webhook.ts, datadog.ts, new-relic.ts — shares the same object instance.

This assignment therefore permanently disables HTML escaping for every other Mustache.render() call in the same Node process (including email-service.ts templates) from the moment any single notification addon (Webhook, Slack legacy, Microsoft Teams, Datadog, New Relic) first formats any event, for the remaining lifetime of the process.

feature-event-formatter-md-events.ts (EVENT_MAP) confirms the blast radius: nearly every event's action template interpolates attacker-controlled values with single-mustache (intended-to-be-escaped) syntax, most importantly {{user}}, which is event.createdBy — the acting account's username (or email if set; src/lib/util/extract-user.ts: extractUsernameFromUser). Neither username nor name have any charset/length validation anywhere in the codebase (create-user-schema.ts, create-invited-user-schema.ts, user-service.ts:289 only does Joi.assert(name, Joi.string(), 'Name') — a type check, nothing more).

Slack's own API docs require &, <, > to be replaced with &amp;, &lt;, &gt; before sending user-generated text, specifically so Slack's mrkdwn parser does not interpret it as <url|label> link syntax. Mustache's default escapeHtml happens to produce exactly those entities, so this was (likely unintentionally) the application's only defense against link-injection in chat notifications — and it is unconditionally switched off by the same code path that depends on it.

Attack Scenario

  1. Admin has a Webhook, Slack (legacy), Microsoft Teams, Datadog, or New Relic integration configured (a very common production setup for flag-change notifications).
  2. Attacker has (or self-registers, if public signup is enabled — POST /invite/:token/signup is permission: NONE) any Editor-level account and sets username to e.g. evil<https://attacker.example/urgent-rollback|Click here to view incident>.
  3. Attacker performs any ordinary write action (create/update/toggle a feature flag — routine, no special privilege beyond Editor on one project).
  4. The configured addon's handleEvent() calls this.msgFormatter.format(event), which mutates the global escape function and immediately renders the {{user}}-containing template with escaping disabled.
  5. The resulting message — containing the attacker's raw <url|label> Slack link syntax — is POSTed to the team's Slack/Teams channel or webhook endpoint and rendered as a real, clickable, attacker-labeled hyperlink inside a trusted automated notification feed.

Vulnerable Code

Mustache.escape = (text) => text;

const text = Mustache.render(action, context);
const url = path
    ? `${this.unleashUrl}${Mustache.render(path, context)}`
    : undefined;

Impact

  • Stored markdown/link-injection (phishing-link injection) into any configured outbound notification channel (Slack legacy, MS Teams, Webhook default markdown, Datadog, New Relic), using an attacker-controlled username — no admin privilege required, only Editor on a single project, and potentially reachable through public self-signup.
  • Secondary: loss of HTML escaping for any other reachable Mustache single-mustache placeholder process-wide until restart (increases severity of any other currently-unreached or future Mustache sink, e.g. email templates).
  • Tertiary: a custom Webhook bodyTemplate that interpolates raw event/user fields directly into a JSON string literal (rather than the pre-escaped eventJson field the code already provides for this purpose) can have its JSON structure broken by an attacker-controlled " character once the global escape function is neutered.

Recommended Fix

Never mutate the shared Mustache.escape global. Pass a local escape function via Mustache's per-call render option instead (supported and typed in @types/[email protected]'s RenderOptions.escape):

const renderConfig = { escape: (text: string) => text };
const text = Mustache.render(action, context, undefined, renderConfig);
const url = path
    ? `${this.unleashUrl}${Mustache.render(path, context, undefined, renderConfig)}`
    : undefined;

Verification

Dynamically confirmed on v8.0.1 in a local Docker lab (official unleashorg/unleash-server:8.0.1 image + Postgres 15):

  • Created a Webhook addon with the addon UI's own placeholder bodyTemplate ({{event.createdBy}} etc.), pointed at a local listener.
  • Created an Editor-role user with username = evil2<https://attacker.example/urgent-rollback|Click here to view incident> (accepted with HTTP 201, no sanitization).
  • Logged in as that user and created a feature flag (ordinary Editor action).
  • Captured webhook payload: "createdBy": "evil2<https://attacker.example/urgent-rollback|Click here to view incident>"<, >, | completely unescaped, live Slack link-injection syntax.
  • Control test with the same pinned [email protected] package confirmed the default (pre-bug) output for the same string would have been evil2&lt;https:&#x2F;&#x2F;attacker.example&#x2F;urgent-rollback|Click here to view incident&gt; — i.e. the single global assignment is solely responsible for the unescaped output observed live.

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
📦npmunleash-serverall versions8.0.3

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for unleash-server. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.

  2. Fix

    Update unleash-server to 8.0.3 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-w4mq-xh27-6xpx is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

  4. How O3 protects you

    O3 pinpoints whether GHSA-w4mq-xh27-6xpx is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.

Tailored to GHSA-w4mq-xh27-6xpx. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.

Frequently Asked Questions

## Vulnerability Details **File**: `src/lib/addons/feature-event-formatter-md.ts` **Line**: 355 (in v8.0.1; `format()` method) ### Root Cause `FeatureEventFormatterMd.format()` does: ```ts Mustache.escape = (text) => text; const text = Mustache.render(action, context); ``` `mustache` (pinned `^4.2.0`, confirmed installed `4.2.0`) keeps `escape` as a **module-level singleton** (`mustache.js`: `mustache.escape = escapeHtml;`), read by every `Mustache.render()` call in the process unless a per-call `config.escape` override is passed (`var escape = this.getConfigEscape(config) || mustache.esc
O3 Security · Impact-Aware SCA

Is GHSA-w4mq-xh27-6xpx in your dependencies?

O3 detects GHSA-w4mq-xh27-6xpx across npm dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.