Description
isCreditCard returns true for the 4-digit string "5108" (and many other short or partial strings) due to a regex anchoring bug in the Mastercard pattern.
Reproduction (validator 13.15.35, the latest version on npm at time of writing)
const v = require("validator");
console.log(v.isCreditCard("5108")); // true — expected false
console.log(v.isCreditCard("5108", { provider: "mastercard" })); // true — expected false
console.log(v.isCreditCard("5105105105105100")); // true — correct
Root cause
The Mastercard regex in src/lib/isCreditCard.js is:
mastercard: /^5[1-5][0-9]{2}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}$/
Because | has lower precedence than ^ and $ anchors, the regex engine actually parses this as the union of two patterns:
^5[1-5][0-9]{2} — start-anchored only, matches any string beginning with 51XX–55XX regardless of length (so "5108", "5108foo", etc. all match).
(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}$ — end-anchored only.
Result: format validation degenerates to "starts with 51-55 anywhere" or "ends with 2-series BIN + 12 digits anywhere".
The same shape (single-anchor due to | precedence) appears in a few other card regexes in this file; worth a quick audit.
Suggested fix
Wrap the alternation in a non-capturing group so both anchors bind to all branches:
- mastercard: /^5[1-5][0-9]{2}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}$/
+ mastercard: /^(?:5[1-5][0-9]{2}|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}$/
After the fix, isCreditCard("5108") correctly returns false, and the valid 16-digit cases continue to return true.
Property that fails
// All credit-card-valid strings must have length within the documented per-issuer length ranges
// (Mastercard: 16; including 19-digit private/extension forms it's still ≥ 13).
function prop(s) {
if (v.isCreditCard(s, { provider: "mastercard" })) {
return s.length >= 13; // every passing input must be at least 13 chars
}
return true;
}
prop("5108"); // false — invariant violated
Environment
- validator:
13.15.35 (also reproduces in 13.12.0, so likely all 13.x)
- Node: 20+
Happy to submit a PR if helpful.
Description
isCreditCardreturnstruefor the 4-digit string"5108"(and many other short or partial strings) due to a regex anchoring bug in the Mastercard pattern.Reproduction (validator 13.15.35, the latest version on npm at time of writing)
Root cause
The Mastercard regex in
src/lib/isCreditCard.jsis:mastercard: /^5[1-5][0-9]{2}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}$/Because
|has lower precedence than^and$anchors, the regex engine actually parses this as the union of two patterns:^5[1-5][0-9]{2}— start-anchored only, matches any string beginning with51XX–55XXregardless of length (so"5108","5108foo", etc. all match).(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}$— end-anchored only.Result: format validation degenerates to "starts with 51-55 anywhere" or "ends with 2-series BIN + 12 digits anywhere".
The same shape (single-anchor due to
|precedence) appears in a few other card regexes in this file; worth a quick audit.Suggested fix
Wrap the alternation in a non-capturing group so both anchors bind to all branches:
After the fix,
isCreditCard("5108")correctly returnsfalse, and the valid 16-digit cases continue to returntrue.Property that fails
Environment
13.15.35(also reproduces in13.12.0, so likely all 13.x)Happy to submit a PR if helpful.