Skip to content

card_number() accepts non-ASCII Unicode digits (full-width, Arabic-Indic) as valid card numbers #567

Open

Description

@rahul05ranjan

card_number() accepts non-ASCII Unicode digits (full-width, Arabic-Indic) as valid card numbers

card_number() computes its Luhn sum with int() on each character, and Python's int() happily accepts any Unicode decimal digit — so a "card number" typed with full-width digits (common with CJK IMEs) or copied from OCR'd/Arabic documents validates as a perfectly good PAN, while every payment processor would reject it.

Repro (current master, 22786cc):

from validators import card_number

# The canonical Visa test number, but written with Arabic-Indic digits (U+0660..U+0669):
arabic = "٤٢٤٢٤٢٤٢٤٢٤٢٤٢٤٢"
print(card_number(arabic))   # True

# Same thing with full-width digits (U+FF10..U+FF19):
fullwidth = "4242424242424242"
print(card_number(fullwidth))  # True

Both strings are the digits of 4242424242424242 remapped onto other Unicode decimal-digit blocks, and both pass card_number() because list(map(int, value)) converts them just fine.

Root cause (src/validators/card.py):

digits = list(map(int, value))

int("4") and int("٤") both succeed, so the Luhn check runs over non-ASCII digit values.

Impact

A full-width-digit card number entered in an East-Asian IME or a Arabic-Indic one pasted from a scanned document is reported as a valid card number, then fails at the payment gateway. For the brand-specific helpers the wrong digits happen to be caught by their ^ prefix regexes (e.g. visa() requires an ASCII 4), but the generic card_number() is the one usually called first, and mixed strings like "4242424242424242" pass the Luhn check too whenever the underlying digit values work out.

Suggested fix

Reject non-ASCII input up front, e.g.:

if not value.isascii():   # or: value.isdigit() and value.isascii()
    return False

This follows the same direction as #532 ("Require ASCII digits in Finnish, French and Indian identifiers") and #563 (the same issue reported for cron()); card_number() looks like the remaining high-visibility spot where non-ASCII digits are still accepted.

Activity

  1. Mrun25 commented on Nov 14, 2026

    @Mrun25

    Opened #568 with the fix and regression tests for Unicode card-number digits.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions