Regex Indian PAN (Permanent Account Number)
The PAN (Permanent Account Number) issued by the Indian tax administration has 10 characters: five letters, four digits, one letter. The administration's FAQ (TRACES) states that the 4th character is the holder type, among A B C F G H J L P T, and that the 5th is the initial of the surname (or of the entity's name). python-stdnum and validator.js also rule out the '0000' serial, a rule absent from the FAQ. The patterns read here each have a flaw: django-localflavor accepts the 0000 serial, python-stdnum only checks the 4th character after the regex, and Presidio, built for detection, is unanchored. Here's an anchored pattern checking type and serial, compared against those attempts.
/^[A-Z]{3}[ABCFGHJLPT][A-Z](?!0000)[0-9]{4}[A-Z]$/passes 22/22 cases
Comparison of existing patterns
| Source | pattern | Score on the corpus | ReDoS |
|---|---|---|---|
| validator.js isTaxID('en-IN') — ancrée, type de titulaire et série 0000 contrôlés : équivalente au motif recommandé, sans défaut constaté sur ce corpus | ^[A-Z]{3}[ABCFGHLJPT][A-Z](?!0000)[0-9]{4}[A-Z]$ | 22 / 22 | safe |
| django-localflavor (in_/forms.py) — type de titulaire contrôlé mais la série « 0000 » passe | ^[A-Z]{3}[ABCFGHLJPT][A-Z][0-9]{4}[A-Z]$ | 21 / 22 | safe |
| python-stdnum (in_/pan.py) — le 4e caractère n'est pas contrôlé par la regex (« ABCDE1234F » passe ; le module le vérifie ensuite) et la série 0000 passe | ^[A-Z]{5}[0-9]{4}[A-Z]$ | 18 / 22 | safe |
| Presidio, motif « PAN (High) » — détection par \b, non ancrée : « id:ABCPA1234D » et une version en minuscules passent, série 0000 acceptée | \b([A-Za-z]{3}[AaBbCcFfGgHhJjLlPpTt]{1}[A-Za-z]{1}[0-9]{4}[A-Za-z]{1})\b | 18 / 22 | safe |
Implementation
const re = new RegExp("^[A-Z]{3}[ABCFGHJLPT][A-Z](?!0000)[0-9]{4}[A-Z]$", "");
re.test(input);Limits
- Valide seulement la FORME : la lettre finale suit un algorithme non publié, aucune regex ne peut la contrôler, et le 5e caractère (initiale du nom) ne peut pas être vérifié sans le nom.
- La règle « série différente de 0000 » vient de python-stdnum et de validator.js, pas de la FAQ de l'administration, qui ne dit que « quatre chiffres ». Si vous voulez coller strictement à la FAQ, retirez le « (?!0000) ».
- Les lettres majuscules seulement : normalisez la casse avant. Le type « K » (accepté par certaines bibliothèques) est refusé car absent de la liste de la FAQ.
- N'établit pas qu'un PAN a été attribué (la vérification se fait auprès de l'administration). Les PAN du corpus n'ont aucune clé publique : un exemple synthétique peut coïncider avec un vrai numéro, ne les utilisez que comme données de test.