Skip to content

fix(isCreditCard): anchor Mastercard regex to reject partial strings#2741

Open
abhu85 wants to merge 1 commit into
validatorjs:masterfrom
abhu85:fix/2717-mastercard-regex-anchoring
Open

fix(isCreditCard): anchor Mastercard regex to reject partial strings#2741
abhu85 wants to merge 1 commit into
validatorjs:masterfrom
abhu85:fix/2717-mastercard-regex-anchoring

Conversation

@abhu85
Copy link
Copy Markdown

@abhu85 abhu85 commented Jun 6, 2026

Fixes #2717

What

The Mastercard pattern in src/lib/isCreditCard.js used a top-level alternation with no grouping, so the ^ and $ anchors each bound to only one branch:

/^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 the anchors, this parses as two patterns:

  1. ^5[1-5][0-9]{2}start-anchored only, matches any string beginning with 5155 regardless of length.
  2. (222[1-9]|…|2720)[0-9]{12}$end-anchored only.

So isCreditCard("5108") returned true: "5108" matches branch 1 and also passes the Luhn check (5×2=10→1, +0, +1, +8 = 10). Fifty 4-digit Luhn-valid strings in the 5155 range are wrongly accepted.

Fix

Wrap the alternation in a non-capturing group so both anchors apply to every branch, restoring the intended full 16-digit format:

/^(?: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") returns false, and all previously-valid 16-digit Mastercard numbers (5-series and 2-series) continue to validate.

I audited the other card patterns in the file: only mastercard had a correctness bug. (unionpay has a redundant nested ^^ but is functionally correct; left untouched to keep this PR scoped.)

References

Checklist

  • PR contains only changes related; no stray files, etc.
  • README updated (not applicable)
  • Tests written — "5108" added to the no-provider and Mastercard invalid fixtures
  • References provided in PR

The Mastercard pattern used a top-level alternation with no grouping, so
the ^ and $ anchors each bound to only one branch:

  ^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}$

Branch 1 was start-anchored only, so any string beginning with 51-55
(e.g. "5108") that also passed the Luhn check validated as a Mastercard;
branch 2 was end-anchored only. Wrapping the alternation in a
non-capturing group makes both anchors apply to every branch, restoring
the intended full 16-digit format check.
@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 6, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (7fdc788) to head (933fe81).

Additional details and impacted files
@@            Coverage Diff            @@
##            master     #2741   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          114       114           
  Lines         2587      2587           
  Branches       656       656           
=========================================
  Hits          2587      2587           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

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

1 participant