isCreditCard: Mastercard regex anchoring bug accepts 4-digit and partial strings

Open Beginner friendly
#2,717 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
84/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Quiet
Tech stack
javascript
Domain
backend

Research direction

Start in src/lib/isCreditCard.js by reading the Mastercard pattern and checking the other card regexes with alternation. Reproduce the short-string cases from the issue, then verify that Mastercard inputs are rejected when too short while valid 16-digit examples remain accepted. Done means the anchoring behavior is corrected consistently without breaking valid card formats.

Written by the indexing model from the issue text.

Description

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:

  1. ^5[1-5][0-9]{2} — start-anchored only, matches any string beginning with 51XX55XX regardless of length (so "5108", "5108foo", etc. all match).
  2. (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.

Dominant language
JavaScript
Stars
23.7k
Forks
2.5k
PR merge metrics
No merged PRs in 30d

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from validatorjs/validator.js

All issues in validatorjs/validator.js

Similar issues

More JavaScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.