Skip to content

fix(deps): update dependency valibot to v1.4.2 [security] - #727

Open
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/npm-valibot-vulnerability
Open

fix(deps): update dependency valibot to v1.4.2 [security]#727
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/npm-valibot-vulnerability

Conversation

@renovate

@renovate renovate Bot commented Nov 26, 2025

Copy link
Copy Markdown
Contributor

This PR contains the following updates:

Package Change Age Confidence
valibot (source) 1.0.01.4.2 age confidence

Valibot has a ReDoS vulnerability in EMOJI_REGEX

CVE-2025-66020 / GHSA-vqpr-j7v3-hqw9

More information

Details

Summary

The EMOJI_REGEX used in the emoji action is vulnerable to a Regular Expression Denial of Service (ReDoS) attack. A short, maliciously crafted string (e.g., <100 characters) can cause the regex engine to consume excessive CPU time (minutes), leading to a Denial of Service (DoS) for the application.

Details

The ReDoS vulnerability stems from "catastrophic backtracking" in the EMOJI_REGEX. This is caused by ambiguity in the regex pattern due to overlapping character classes.

Specifically, the class \p{Emoji_Presentation} overlaps with more specific classes used in the same alternation, such as [\u{1F1E6}-\u{1F1FF}] (regional indicator symbols used for flags) and \p{Emoji_Modifier_Base}.

When the regex engine attempts to match a string that almost matches but ultimately fails (like the one in the PoC), this ambiguity forces it to explore an exponential number of possible paths. The matching time increases exponentially with the length of the crafted input, rather than linearly.

PoC

The following code demonstrates the vulnerability.

import * as v from 'valibot';

const schema = v.object({
  x: v.pipe(v.string(), v.emoji()),
});

const attackString = '\u{1F1E6}'.repeat(49) + '0';

console.log(`Input length: ${attackString.length}`);
console.log('Starting parse... (This will take a long time)');

// On my machine, a length of 99 takes approximately 2 minutes.
console.time();
try {
  v.parse(schema, {x: attackString });
} catch (e) {}
console.timeEnd();
Impact

Any project using Valibot's emoji validation on user-controllable input is vulnerable to a Denial of Service attack.

An attacker can block server resources (e.g., a web server's event loop) by submitting a short string to any endpoint that uses this validation. This is particularly dangerous because the attack string is short enough to bypass typical input length restrictions (e.g., maxLength(100)).

Recommended Fix

The root cause is the overlapping character classes. This can be resolved by making the alternatives mutually exclusive, typically by using negative lookaheads ((?!...)) to subtract the specific classes from the more general one.

The following modified EMOJI_REGEX applies this principle:

export const EMOJI_REGEX: RegExp =
  // eslint-disable-next-line redos-detector/no-unsafe-regex, regexp/no-dupe-disjunctions -- false positives
  /^(?:[\u{1F1E6}-\u{1F1FF}]{2}|\u{1F3F4}[\u{E0061}-\u{E007A}]{2}[\u{E0030}-\u{E0039}\u{E0061}-\u{E007A}]{1,3}\u{E007F}|(?:\p{Emoji}\uFE0F\u20E3?|\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|(?![\p{Emoji_Modifier_Base}\u{1F1E6}-\u{1F1FF}])\p{Emoji_Presentation})(?:\u200D(?:\p{Emoji}\uFE0F\u20E3?|\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|(?![\p{Emoji_Modifier_Base}\u{1F1E6}-\u{1F1FF}])\p{Emoji_Presentation}))*)+$/u;

Severity

  • CVSS Score: 7.5 / 10 (High)
  • Vector String: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


Valibot: record() issue paths can make flatten() throw for inherited Object property names

CVE-2026-59952 / GHSA-5qjj-4xww-7phc

More information

Details

Summary

valibot 1.4.1 can throw a TypeError inside its flatten() helper when validation issues contain attacker-controlled object keys such as toString, valueOf, or hasOwnProperty.

The issue is reachable through normal record() validation. record() intentionally filters __proto__, prototype, and constructor, but it still accepts other own keys that collide with inherited Object.prototype properties. If the record key schema or value schema rejects such an entry, Valibot creates an issue path containing that key. Passing the resulting issues to Valibot's documented flatten() helper causes flatErrors.nested[dotPath] to resolve to the inherited method instead of an own error array, and the helper calls .push(...) on that function.

This is not a global prototype pollution issue. The impact is availability/error handling: applications that validate user-controlled objects with record() and flatten validation errors for API responses can crash the request path with a TypeError instead of returning structured validation errors.

Affected package
  • Ecosystem: npm
  • Package: valibot
  • Affected version verified: 1.4.1
  • Fixed version: none known
  • Repository: open-circle/valibot
  • Current main ref tested by source review: 9bb6617
Root cause

record() uses _isValidObjectKey() before validating record entries. The helper blocks the three classic prototype pollution keys:

key !== '__proto__' &&
key !== 'prototype' &&
key !== 'constructor'

It does not block other inherited Object.prototype names such as toString, valueOf, and hasOwnProperty. These remain valid own JSON object keys and can appear in issue paths when either the record key schema or value schema rejects the entry.

flatten() then creates nested error storage with an ordinary object:

flatErrors.nested = {};

For a dot path such as toString, this check reads the inherited Object.prototype.toString function:

if (flatErrors.nested![dotPath]) {
  flatErrors.nested![dotPath]!.push(issue.message);
}

Because the inherited function is truthy, flatten() calls .push(...) on a function and throws TypeError: flatErrors.nested[dotPath].push is not a function.

Impact

A remote attacker can trigger this if an application:

  1. validates attacker-controlled JSON objects with v.record(...);
  2. receives an invalid key or invalid value under a key such as toString;
  3. uses Valibot's flatten(result.issues) helper to prepare validation errors.

This is a common pattern in API/form validation: safeParse() collects issues and flatten() converts them into response-friendly error objects. Instead of a validation response, the request can hit an unexpected exception path.

The same root cause can also affect manually constructed issues or other schemas that place inherited Object property names into dot paths. I am reporting the record() path because it uses only public Valibot APIs and attacker-controlled JSON keys.

Local reproduction

Run in a disposable directory:

npm install valibot@1.4.1
node poc_record_flatten_inherited_key_dos.mjs

Minimal example:

import * as v from 'valibot';

const schema = v.record(v.string(), v.number());
const input = JSON.parse('{"toString":"not-a-number"}');

const result = v.safeParse(schema, input);
console.log(result.success); // false
console.log(result.issues[0].path.map((item) => item.key)); // ["toString"]

v.flatten(result.issues); // TypeError

Observed output from valibot@1.4.1:

{
  "name": "record value schema rejects attacker-controlled value",
  "key": "toString",
  "success": false,
  "issueCount": 1,
  "firstPath": ["toString"],
  "firstMessage": "Invalid type: Expected number but received \"not-a-number\"",
  "flattened": {
    "ok": false,
    "exception": "TypeError",
    "message": "flatErrors.nested[dotPath].push is not a function"
  }
}

The local PoC also reproduces the same exception for valueOf, hasOwnProperty, isPrototypeOf, propertyIsEnumerable, and toLocaleString. A control case with an ordinary key produces normal flattened errors.

Duplicate checks performed before submission
  • npm metadata confirmed current valibot release is 1.4.1 and maps to open-circle/valibot.
  • gh api repos/open-circle/valibot/private-vulnerability-reporting returned {"enabled":true}.
  • npm audit for a clean project containing only valibot@1.4.1 returned no vulnerabilities.
  • Repository advisories and the GitHub Advisory Database only returned the historical emoji ReDoS advisory fixed in 1.2.0.
  • OSV exact-version query for npm valibot 1.4.1 returned no vulnerabilities.
  • Public issue/PR searches for flatten toString, flatten hasOwnProperty, record toString, __proto__, constructor, and prototype pollution did not find a matching disclosure of this record() issue-path / flatten() exception.
  • Reviewed related public PRs: open-circle/valibot#67 added prototype pollution mitigation for record() by blacklisting __proto__, prototype, and constructor; it does not cover flatten() collisions with other inherited property names. open-circle/valibot#1429 is an open plain-object / record() type semantics PR and does not disclose this flatten() exception behavior.
Suggested remediation

Use null-prototype containers for flat error maps and/or perform own-property checks before appending:

  • Initialize flatErrors.nested as Object.create(null).
  • Check nested entries with Object.prototype.hasOwnProperty.call(flatErrors.nested, dotPath) rather than truthiness.
  • Consider filtering or escaping unsafe dot path segments in getDotPath() / flatten(), including inherited Object property names.
  • Add regression tests for flatten() with paths toString, valueOf, hasOwnProperty, __proto__, prototype, and constructor.
  • Consider using the same hardening for other accumulator objects that store attacker-controlled keys.

Severity

  • CVSS Score: 6.9 / 10 (Medium)
  • Vector String: CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


Release Notes

open-circle/valibot (valibot)

v1.4.2

Compare Source

Many thanks to @​Faze-up and @​chatman-media for contributing to this release.

  • Fix word count actions to cache the Intl.Segmenter for non-primitive locales, preventing it from being recreated on every words, minWords, maxWords and notWords validation (pull request #​1521)
  • Fix flatten method to handle issue path keys that collide with Object.prototype members like toString instead of throwing a TypeError (pull request #​1522)
  • Fix intersect schema to merge object keys that collide with Object.prototype members like toString instead of failing to merge them (pull request #​1522)

v1.4.1

Compare Source

  • Fix intersect schema to infer correct input and output types for non-tuple array options instead of never (pull request #​1478)

v1.4.0

Compare Source

Many thanks to @​ksaurav24, @​heiwen, @​compulim, @​ysknsid25, @​alaycock-stripe, @​IlyaSemenov, @​wszgrcy, @​LMGO, @​yslpn, @​EltonLobo07 and @​Eronmmer for contributing to this release.

Read the release notes on our website for a quick overview of the most exciting new features in this release.

  • Add isoDateTimeSecond validation action to validate ISO date times with seconds (pull request #​1418)
  • Add toCamelCase, toKebabCase, toPascalCase and toSnakeCase transformation actions to convert strings between common naming conventions (pull request #​1457)
  • Change internal ReadonlyOutputKeys and OutputWithReadonly types of object schemas and WithReadonly type of record schemas to improve TypeScript type performance (pull request #​1442)
  • Change hot paths to reduce object allocations and improve runtime performance (pull request #​1437)
  • Change build target to ES2020 so distributed output stays compatible with environments that lack support for newer syntax (pull request #​1455)
  • Change internal _LruCache to use a TypeScript private method instead of a #private class field to avoid runtime helpers in the transpiled output (pull request #​1455)
  • Change internal _isValidObjectKey to use Object.prototype.hasOwnProperty.call instead of Object.hasOwn so the distributed output stays compatible with runtimes that lack the ES2022 Object.hasOwn builtin (pull request #​1421)
  • Change flatten method to accept readonly issue arrays (pull request #​1269)
  • Fix potential RangeError caused by spreading large issue arrays (pull request #​1437)
  • Fix creditCard validation action to reject Mastercard numbers with invalid lengths (pull request #​1462)
  • Fix intersect schema to no longer mutate input values, allowing frozen objects and arrays to be merged (pull request #​1463)

v1.3.1

Compare Source

  • Change MAC48_REGEX, MAC64_REGEX and MAC_REGEX to drop the i flag for better JSON Schema compatibility (pull request #​1430)
  • Change hash action to use case-expanded character classes instead of the i flag (pull request #​1430)

v1.3.0

Compare Source

Many thanks to @​EskiMojo14, @​yslpn, @​alexilyaev, @​idleberg, @​BerkliumBirb and @​frenzzy for contributing to this release.

Read the release notes on our website for a quick overview of the most exciting new features in this release.

  • Add guard transformation action to narrow types using type predicates (pull request #​1204)
  • Add parseBoolean transformation action to parse boolean values from strings and other types (pull request #​1251)
  • Add isrc validation action to validate ISRC codes (pull request #​1373)
  • Add cache method for caching schema output by input (pull request #​1170)
  • Add domain validation action to validate domain names (pull request #​1284)
  • Add jwsCompact validation action to validate JWS compact strings (pull request #​1348)
  • Fix creditCard validation action to allow 13-digit Visa card numbers (pull request #​1347)
  • Fix isoTimestamp validation action to allow optional space before UTC offset for PostgreSQL timestamptz compatibility (pull request #​1195)
  • Fix types for deeply readonly default and fallback values

v1.2.0

Compare Source

Many thanks to @​EskiMojo14, @​makenowjust, @​ysknsid25 and @​jacekwilczynski for contributing to this release.

Read the release notes on our website for a quick overview of the most exciting new features in this release.

  • Add toBigint, toBoolean, toDate, toNumber and toString transformation actions (pull request #​1212)
  • Add examples action to add example values to a schema (pull request #​1199)
  • Add getExamples method to extract example values from a schema (pull request #​1199)
  • Add isbn validation action to validate ISBN-10 and ISBN-13 strings (pull request #​1097)
  • Add exports for RawCheckAddIssue, RawCheckContext, RawCheckIssueInfo, RawTransformAddIssue, RawTransformContext and RawTransformIssueInfo types for better developer experience with rawCheck and rawTransform actions (pull request #​1359)
  • Change build step to tsdown
  • Fix ReDoS vulnerability in EMOJI_REGEX used by emoji action

v1.1.0

Compare Source

Many thanks to @​EltonLobo07, @​sacrosanctic, @​muningis, @​EskiMojo14, @​MOZGIII, @​vktrl and @​jasperteo for contributing to this release.

Read the release notes on our website for a quick overview of the most exciting new features in this release.

  • Add message method to overwrite local error message configuration of a schema (pull request #​1103)
  • Add summarize method to summarize issues into a pretty-printable multi-line string (pull request #​1158)
  • Add getTitle, getDescription and getMetadata methods to extract metadata of a schema (pull request #​1154)
  • Add minEntries and maxEntries validation action to validate number of object entries (pull request #​1100)
  • Add entries and notEntries validation action to validate number of object entries (pull request #​1156)
  • Add parseJson and stringifyJson transformation action to parse and stringify JSON (pull request #​1137)
  • Add flavor transformation action to flavor the output type of a schema (pull request #​950)
  • Add support for bigints to multipleOf validation action (pull request #​1164)
  • Change implementation of variant and variantAsync schema to improve performance by aborting validation of discriminators early (pull request #​1110)
  • Change name of NanoIDAction and NanoIDIssue interface to NanoIdAction and NanoIdIssue (pull request #​1171)
  • Fix internal MarkOptional type to fix input and output type of objects in edge cases (issue #​1176)

Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Enabled.

Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate
renovate Bot enabled auto-merge (squash) November 26, 2025 21:59
@renovate
renovate Bot requested a review from koyashiro November 27, 2025 03:48
@renovate
renovate Bot force-pushed the renovate/npm-valibot-vulnerability branch 10 times, most recently from bc30ce5 to cd33df6 Compare December 3, 2025 13:27
@renovate
renovate Bot force-pushed the renovate/npm-valibot-vulnerability branch 9 times, most recently from 7a4ea89 to e8c396b Compare December 10, 2025 11:14
@renovate
renovate Bot force-pushed the renovate/npm-valibot-vulnerability branch 4 times, most recently from d448186 to 622e4ad Compare December 15, 2025 21:51
@renovate
renovate Bot force-pushed the renovate/npm-valibot-vulnerability branch 4 times, most recently from 4473eff to fed3879 Compare December 24, 2025 05:58
@renovate
renovate Bot force-pushed the renovate/npm-valibot-vulnerability branch 7 times, most recently from 6d45259 to 025c34e Compare January 5, 2026 21:45
@renovate
renovate Bot force-pushed the renovate/npm-valibot-vulnerability branch 9 times, most recently from 698f8ac to 21a36b8 Compare January 15, 2026 05:10
@renovate
renovate Bot force-pushed the renovate/npm-valibot-vulnerability branch from 21a36b8 to 976dc8f Compare January 16, 2026 06:47
@renovate
renovate Bot force-pushed the renovate/npm-valibot-vulnerability branch from 976dc8f to fe3d0f8 Compare February 2, 2026 15:05
@renovate renovate Bot changed the title fix(deps): update dependency valibot to v1.2.0 [security] fix(deps): update dependency valibot to v1.2.0 [security] - autoclosed Mar 27, 2026
@renovate renovate Bot closed this Mar 27, 2026
auto-merge was automatically disabled March 27, 2026 01:34

Pull request was closed

@renovate
renovate Bot deleted the renovate/npm-valibot-vulnerability branch March 27, 2026 01:34
@renovate renovate Bot changed the title fix(deps): update dependency valibot to v1.2.0 [security] - autoclosed fix(deps): update dependency valibot to v1.2.0 [security] Mar 30, 2026
@renovate renovate Bot reopened this Mar 30, 2026
@renovate
renovate Bot force-pushed the renovate/npm-valibot-vulnerability branch 2 times, most recently from fe3d0f8 to c4b97cb Compare March 30, 2026 22:11
@renovate
renovate Bot force-pushed the renovate/npm-valibot-vulnerability branch from c4b97cb to c8f3df9 Compare July 28, 2026 09:37
@renovate
renovate Bot enabled auto-merge (squash) July 28, 2026 09:37
@renovate renovate Bot changed the title fix(deps): update dependency valibot to v1.2.0 [security] fix(deps): update dependency valibot to v1.4.2 [security] Jul 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants