{"containers":{"cna":{"affected":[{"collectionURL":"https://repo.hex.pm","cpes":["cpe:2.3:a:lud:jsv:*:*:*:*:*:*:*:*"],"defaultStatus":"unaffected","modules":["'Elixir.JSV.Helpers.RegexExt'","'Elixir.JSV.Vocabulary.V202012.Validation'","'Elixir.JSV.Vocabulary.V202012.Applicator'","'Elixir.JSV'"],"packageName":"jsv","packageURL":"pkg:hex/jsv","product":"jsv","programFiles":["lib/jsv/helpers/regex_ext.ex","lib/jsv/vocabulary/v202012/validation.ex","lib/jsv/vocabulary/v202012/applicator.ex","lib/jsv.ex"],"programRoutines":[{"name":"'Elixir.JSV.Helpers.RegexExt':'bounded_match?'/2"},{"name":"'Elixir.JSV.Vocabulary.V202012.Validation':validate_keyword/3"},{"name":"'Elixir.JSV.Vocabulary.V202012.Applicator':validate_keyword/3"},{"name":"'Elixir.JSV':validate/2"},{"name":"'Elixir.JSV':validate/3"}],"repo":"https://github.com/lud/jsv","vendor":"lud","versions":[{"lessThan":"0.26.2","status":"affected","version":"0.1.0","versionType":"semver"}]},{"collectionURL":"https://github.com","cpes":["cpe:2.3:a:lud:jsv:*:*:*:*:*:*:*:*"],"defaultStatus":"unaffected","modules":["'Elixir.JSV.Helpers.RegexExt'","'Elixir.JSV.Vocabulary.V202012.Validation'","'Elixir.JSV.Vocabulary.V202012.Applicator'","'Elixir.JSV'"],"packageName":"lud/jsv","packageURL":"pkg:github/lud/jsv","product":"jsv","programFiles":["lib/jsv/helpers/regex_ext.ex","lib/jsv/vocabulary/v202012/validation.ex","lib/jsv/vocabulary/v202012/applicator.ex","lib/jsv.ex"],"programRoutines":[{"name":"'Elixir.JSV.Helpers.RegexExt':'bounded_match?'/2"},{"name":"'Elixir.JSV.Vocabulary.V202012.Validation':validate_keyword/3"},{"name":"'Elixir.JSV.Vocabulary.V202012.Applicator':validate_keyword/3"},{"name":"'Elixir.JSV':validate/2"},{"name":"'Elixir.JSV':validate/3"}],"repo":"https://github.com/lud/jsv","vendor":"lud","versions":[{"lessThan":"defdb8a68ac7f218aa5dfe85190d484017cb8817","status":"affected","version":"302a5d0803468f81a1cf08e3b67d8b14cee759ca","versionType":"git"}]}],"configurations":[{"lang":"en","supportingMedia":[{"base64":false,"type":"text/html","value":"<p>The schema must contain a <code>pattern</code> or <code>patternProperties</code> regex that PCRE can match only after exploring a catastrophic branch first, such as a nested quantifier in an earlier alternative. Linear patterns never exhaust the budget. The attacker controls the validated data but not the schema.</p>"},{"base64":false,"type":"text/markdown","value":"The schema must contain a `pattern` or `patternProperties` regex that PCRE can match only after exploring a catastrophic branch first, such as a nested quantifier in an earlier alternative. Linear patterns never exhaust the budget. The attacker controls the validated data but not the schema."}],"value":"The schema must contain a pattern or patternProperties regex that PCRE can match only after exploring a catastrophic branch first, such as a nested quantifier in an earlier alternative. Linear patterns never exhaust the budget. The attacker controls the validated data but not the schema."}],"cpeApplicability":[{"nodes":[{"cpeMatch":[{"criteria":"cpe:2.3:a:lud:jsv:*:*:*:*:*:*:*:*","versionEndExcluding":"0.26.2","versionStartIncluding":"0.1.0","vulnerable":true}],"negate":false,"operator":"OR"}],"operator":"AND"}],"credits":[{"lang":"en","type":"finder","value":"Lars Hesel Christensen"},{"lang":"en","type":"reporter","value":"Lars Hesel Christensen"},{"lang":"en","type":"remediation developer","value":"Ludovic Dem"},{"lang":"en","type":"coordinator","value":"Jonatan Männchen / EEF"}],"dateAssigned":"2026-10-30T23:20:38.000Z","descriptions":[{"lang":"en","supportingMedia":[{"base64":false,"type":"text/html","value":"<p>Improper Check for Unusual or Exceptional Conditions vulnerability in the <code>pattern</code> and <code>patternProperties</code> keywords in lud jsv allows an unauthenticated remote attacker to bypass schema validation via a string that matches a schema regex only after heavy backtracking. <code>JSV.Helpers.RegexExt.bounded_match?/2</code> runs schema regexes with a backtracking budget but calls <code>:re.run/3</code> without <code>:report_errors</code>, so a match that exhausts the budget returns the same <code>:nomatch</code> as a real non-match. A key that matches a <code>patternProperties</code> regex is then checked against <code>additionalProperties</code> or not at all, and a string that matches a <code>not</code> subschema with a <code>pattern</code> is accepted. Before the budget existed, <code>Regex.match?/2</code> failed the same way at Erlang's default match limit.</p>\n<p>Only schemas whose regex explores a catastrophic branch before the branch that matches are affected. The attacker controls the data, not the schema, and linear patterns never exhaust the budget.</p>\n<p>This issue affects jsv: from 0.1.0 before 0.26.2.</p>"},{"base64":false,"type":"text/markdown","value":"Improper Check for Unusual or Exceptional Conditions vulnerability in the `pattern` and `patternProperties` keywords in lud jsv allows an unauthenticated remote attacker to bypass schema validation via a string that matches a schema regex only after heavy backtracking. `JSV.Helpers.RegexExt.bounded_match?/2` runs schema regexes with a backtracking budget but calls `:re.run/3` without `:report_errors`, so a match that exhausts the budget returns the same `:nomatch` as a real non-match. A key that matches a `patternProperties` regex is then checked against `additionalProperties` or not at all, and a string that matches a `not` subschema with a `pattern` is accepted. Before the budget existed, `Regex.match?/2` failed the same way at Erlang's default match limit.\n\nOnly schemas whose regex explores a catastrophic branch before the branch that matches are affected. The attacker controls the data, not the schema, and linear patterns never exhaust the budget.\n\nThis issue affects jsv: from 0.1.0 before 0.26.2."}],"value":"Improper Check for Unusual or Exceptional Conditions vulnerability in the pattern and patternProperties keywords in lud jsv allows an unauthenticated remote attacker to bypass schema validation via a string that matches a schema regex only after heavy backtracking. JSV.Helpers.RegexExt.bounded_match?/2 runs schema regexes with a backtracking budget but calls :re.run/3 without :report_errors, so a match that exhausts the budget returns the same :nomatch as a real non-match. A key that matches a patternProperties regex is then checked against additionalProperties or not at all, and a string that matches a not subschema with a pattern is accepted. Before the budget existed, Regex.match?/2 failed the same way at Erlang's default match limit.\n\nOnly schemas whose regex explores a catastrophic branch before the branch that matches are affected. The attacker controls the data, not the schema, and linear patterns never exhaust the budget.\n\nThis issue affects jsv: from 0.1.0 before 0.26.2."}],"impacts":[{"capecId":"CAPEC-153","descriptions":[{"lang":"en","supportingMedia":[{"base64":false,"type":"text/html","value":"<p>An attacker pads a string until its match exhausts the budget, so a value under a <code>patternProperties</code> key skips its subschema or a string passes a <code>not</code> deny-list. The application then accepts input its schema should reject.</p>"},{"base64":false,"type":"text/markdown","value":"An attacker pads a string until its match exhausts the budget, so a value under a `patternProperties` key skips its subschema or a string passes a `not` deny-list. The application then accepts input its schema should reject."}],"value":"An attacker pads a string until its match exhausts the budget, so a value under a patternProperties key skips its subschema or a string passes a not deny-list. The application then accepts input its schema should reject."}]}],"metrics":[{"cvssV4_0":{"Automatable":"NOT_DEFINED","Recovery":"NOT_DEFINED","Safety":"NOT_DEFINED","attackComplexity":"LOW","attackRequirements":"PRESENT","attackVector":"LOCAL","baseScore":2.1,"baseSeverity":"LOW","privilegesRequired":"NONE","providerUrgency":"NOT_DEFINED","subAvailabilityImpact":"NONE","subConfidentialityImpact":"NONE","subIntegrityImpact":"NONE","userInteraction":"NONE","valueDensity":"NOT_DEFINED","vectorString":"CVSS:4.0/AV:L/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N","version":"4.0","vulnAvailabilityImpact":"NONE","vulnConfidentialityImpact":"NONE","vulnIntegrityImpact":"LOW","vulnerabilityResponseEffort":"NOT_DEFINED"},"format":"CVSS","scenarios":[{"lang":"en","value":"GENERAL"}]}],"problemTypes":[{"descriptions":[{"cweId":"CWE-754","description":"CWE-754 Improper Check for Unusual or Exceptional Conditions","lang":"en","type":"CWE"}]}],"providerMetadata":{"dateUpdated":"2026-11-01T13:29:36.681Z","orgId":"6b3ad84c-e1a6-4bf7-a703-f496b71e49db","shortName":"EEF"},"references":[{"name":"Regex match budget exhaustion is treated as a non-match, bypassing `patternProperties` and `not`","tags":["vendor-advisory","related"],"url":"https://github.com/lud/jsv/security/advisories/GHSA-cvmg-hgv6-jqcp"},{"name":"EEF CNA record for CVE-2026-117680","tags":["related"],"url":"https://cna.erlef.org/cves/CVE-2026-117680.html"},{"name":"OSV record EEF-CVE-2026-117680","tags":["related"],"url":"https://osv.dev/vulnerability/EEF-CVE-2026-117680"},{"name":"Introducing commit 302a5d0 in lud/jsv","tags":["related"],"url":"https://github.com/lud/jsv/commit/302a5d0803468f81a1cf08e3b67d8b14cee759ca"},{"name":"Fix commit defdb8a in lud/jsv","tags":["patch"],"url":"https://github.com/lud/jsv/commit/defdb8a68ac7f218aa5dfe85190d484017cb8817"}],"source":{"discovery":"EXTERNAL"},"title":"jsv treats regex match budget exhaustion as a non-match, bypassing patternProperties and not","workarounds":[{"lang":"en","supportingMedia":[{"base64":false,"type":"text/html","value":"<p>Pair <code>patternProperties</code> with <code>additionalProperties: false</code>. A key whose match exhausts the budget is then rejected instead of accepted unchecked. Valid keys that need heavy backtracking are rejected as well.</p>\n<p>Write schema regexes without nested quantifiers or overlapping alternatives, and do not express deny-lists as a <code>not</code> subschema with a <code>pattern</code> on untrusted input.</p>"},{"base64":false,"type":"text/markdown","value":"Pair `patternProperties` with `additionalProperties: false`. A key whose match exhausts the budget is then rejected instead of accepted unchecked. Valid keys that need heavy backtracking are rejected as well.\n\nWrite schema regexes without nested quantifiers or overlapping alternatives, and do not express deny-lists as a `not` subschema with a `pattern` on untrusted input."}],"value":"Pair patternProperties with additionalProperties: false. A key whose match exhausts the budget is then rejected instead of accepted unchecked. Valid keys that need heavy backtracking are rejected as well.\n\nWrite schema regexes without nested quantifiers or overlapping alternatives, and do not express deny-lists as a not subschema with a pattern on untrusted input."}],"x_proofOfConcept":[{"lang":"en","supportingMedia":[{"base64":false,"type":"text/html","value":"<ol>\n<li>Build a schema with <code>patternProperties</code> mapping the regex <code>^(?:(a+)+b|a+!)$</code> to <code>{\"type\": \"integer\"}</code>.</li>\n<li>Submit an object whose only key is twenty <code>a</code> characters followed by <code>!</code> and whose value is the string <code>not an int</code>. The key matches the regex through its second alternative, but PCRE exhausts the 11,050-step budget in the first alternative.</li>\n<li><code>JSV.validate/2</code> returns <code>:ok</code>. The value is never checked against the subschema.</li>\n<li>Build a second schema <code>{\"not\": {\"pattern\": \"^(?:(a+)+b|a+!)$\"}}</code> and submit the same string. <code>JSV.validate/2</code> returns <code>:ok</code> although the string matches the denied pattern.</li>\n<li>Repeat with the short key <code>aaa!</code>. Both schemas now reject the data as expected.</li>\n</ol>"},{"base64":false,"type":"text/markdown","value":"1. Build a schema with `patternProperties` mapping the regex `^(?:(a+)+b|a+!)$` to `{\"type\": \"integer\"}`.\n2. Submit an object whose only key is twenty `a` characters followed by `!` and whose value is the string `not an int`. The key matches the regex through its second alternative, but PCRE exhausts the 11,050-step budget in the first alternative.\n3. `JSV.validate/2` returns `:ok`. The value is never checked against the subschema.\n4. Build a second schema `{\"not\": {\"pattern\": \"^(?:(a+)+b|a+!)$\"}}` and submit the same string. `JSV.validate/2` returns `:ok` although the string matches the denied pattern.\n5. Repeat with the short key `aaa!`. Both schemas now reject the data as expected."}],"value":"* Build a schema with patternProperties mapping the regex ^(?:(a+)+b|a+!)$ to {\"type\": \"integer\"}.\n* Submit an object whose only key is twenty a characters followed by ! and whose value is the string not an int. The key matches the regex through its second alternative, but PCRE exhausts the 11,050-step budget in the first alternative.\n* JSV.validate/2 returns :ok. The value is never checked against the subschema.\n* Build a second schema {\"not\": {\"pattern\": \"^(?:(a+)+b|a+!)$\"}} and submit the same string. JSV.validate/2 returns :ok although the string matches the denied pattern.\n* Repeat with the short key aaa!. Both schemas now reject the data as expected."}],"x_technicalAnalysis":[{"lang":"en","supportingMedia":[{"base64":false,"type":"text/html","value":"<p><strong>1. Bounded matching.</strong> <code>JSV.Helpers.RegexExt.bounded_match?/2</code> in <code>lib/jsv/helpers/regex_ext.ex</code> runs each schema regex through <code>:re.run/3</code> with <code>match_limit</code> and <code>match_limit_recursion</code> set to <code>10_000 + 50 * byte_size(subject)</code>, capped at 10,000,000. The call omits <code>:report_errors</code>, so PCRE's <code>match_limit</code> error is returned as <code>:nomatch</code>. The function compares the result to <code>:match</code> and returns <code>false</code> for a genuine non-match and for an exhausted budget alike.</p>\n<p><strong>2. Keyword evaluation.</strong> <code>JSV.Vocabulary.V202012.Validation.validate_keyword/3</code> uses the result for <code>pattern</code>, and <code>JSV.Vocabulary.V202012.Applicator</code> uses it to decide which <code>patternProperties</code> subschemas apply to each key. The draft 7 vocabulary delegates to the same modules. A <code>false</code> from an exhausted budget therefore means a <code>pattern</code> keyword fails and a <code>patternProperties</code> subschema is skipped.</p>\n<p><strong>3. Inversion by combinators.</strong> Under <code>not</code>, <code>anyOf</code>, <code>oneOf</code>, <code>if</code> and <code>contains</code>, a failing subschema can make the parent succeed, so a false non-match under <code>not</code> accepts a string the deny-list should reject. Under <code>patternProperties</code> the skipped subschema means the value is checked against <code>additionalProperties</code> or nothing.</p>\n<p><strong>4. History.</strong> Before the budget was introduced, the same keywords called <code>Regex.match?/2</code>, which also returns <code>false</code> on Erlang's default limit of 10,000,000 steps. The budget lowered the input size needed from about 25 bytes to about 20 bytes for the advisory's pattern.</p>"},{"base64":false,"type":"text/markdown","value":"**1. Bounded matching.** `JSV.Helpers.RegexExt.bounded_match?/2` in `lib/jsv/helpers/regex_ext.ex` runs each schema regex through `:re.run/3` with `match_limit` and `match_limit_recursion` set to `10_000 + 50 * byte_size(subject)`, capped at 10,000,000. The call omits `:report_errors`, so PCRE's `match_limit` error is returned as `:nomatch`. The function compares the result to `:match` and returns `false` for a genuine non-match and for an exhausted budget alike.\n\n**2. Keyword evaluation.** `JSV.Vocabulary.V202012.Validation.validate_keyword/3` uses the result for `pattern`, and `JSV.Vocabulary.V202012.Applicator` uses it to decide which `patternProperties` subschemas apply to each key. The draft 7 vocabulary delegates to the same modules. A `false` from an exhausted budget therefore means a `pattern` keyword fails and a `patternProperties` subschema is skipped.\n\n**3. Inversion by combinators.** Under `not`, `anyOf`, `oneOf`, `if` and `contains`, a failing subschema can make the parent succeed, so a false non-match under `not` accepts a string the deny-list should reject. Under `patternProperties` the skipped subschema means the value is checked against `additionalProperties` or nothing.\n\n**4. History.** Before the budget was introduced, the same keywords called `Regex.match?/2`, which also returns `false` on Erlang's default limit of 10,000,000 steps. The budget lowered the input size needed from about 25 bytes to about 20 bytes for the advisory's pattern."}],"value":"1. Bounded matching. JSV.Helpers.RegexExt.bounded_match?/2 in lib/jsv/helpers/regex_ext.ex runs each schema regex through :re.run/3 with match_limit and match_limit_recursion set to 10_000 + 50 * byte_size(subject), capped at 10,000,000. The call omits :report_errors, so PCRE's match_limit error is returned as :nomatch. The function compares the result to :match and returns false for a genuine non-match and for an exhausted budget alike.\n\n2. Keyword evaluation. JSV.Vocabulary.V202012.Validation.validate_keyword/3 uses the result for pattern, and JSV.Vocabulary.V202012.Applicator uses it to decide which patternProperties subschemas apply to each key. The draft 7 vocabulary delegates to the same modules. A false from an exhausted budget therefore means a pattern keyword fails and a patternProperties subschema is skipped.\n\n3. Inversion by combinators. Under not, anyOf, oneOf, if and contains, a failing subschema can make the parent succeed, so a false non-match under not accepts a string the deny-list should reject. Under patternProperties the skipped subschema means the value is checked against additionalProperties or nothing.\n\n4. History. Before the budget was introduced, the same keywords called Regex.match?/2, which also returns false on Erlang's default limit of 10,000,000 steps. The budget lowered the input size needed from about 25 bytes to about 20 bytes for the advisory's pattern."}]}},"cveMetadata":{"assignerOrgId":"6b3ad84c-e1a6-4bf7-a703-f496b71e49db","assignerShortName":"EEF","cveId":"CVE-2026-117680","datePublished":"2026-11-01T13:29:36.681Z","dateReserved":"2026-10-30T12:45:01.930Z","dateUpdated":"2026-11-01T13:29:36.681Z","state":"PUBLISHED"},"dataType":"CVE_RECORD","dataVersion":"5.2"}