isTaxID(dk-DK): valid CPR numbers wrongly rejected for century-digit 5-8, year 37-58

Open Beginner friendly
#2,818 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

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

Research direction

Start in src/lib/isTaxID.js at the dkDkCheck century_digit switch and review the existing year mapping for digits 5-8. Exercise isTaxID with the checksum-valid CPR examples in the issue, especially years 37-58, and confirm that the documented gap-free mapping accepts them while existing cases remain valid.

Written by the indexing model from the issue text.

Description

Description

isTaxID(str, 'dk-DK') (Danish CPR-nummer) incorrectly rejects syntactically- and checksum-valid CPR numbers whenever the 7th digit (century digit) is 5, 6, 7, or 8 and the 2-digit birth year is in the range 37-58 (inclusive).

Root cause

In src/lib/isTaxID.js, dkDkCheck:

switch (century_digit) {
  case '0':
  case '1':
  case '2':
  case '3':
    year = `19${year}`;
    break;
  case '4':
  case '9':
    if (year < 37) {
      year = `20${year}`;
    } else {
      year = `19${year}`;
    }
    break;
  default: // century_digit is 5, 6, 7, or 8
    if (year < 37) {
      year = `20${year}`;
    } else if (year > 58) {
      year = `18${year}`;
    } else {
      return false;
    }
    break;
}

The case '4': case '9': branch correctly implements the documented rule for those digits (year 00-36 → 2000s, year 37-99 → 1900s).

For century digits 5, 6, 7, 8, the documented rule (see e.g. the century-digit table reproduced at https://blog.ploeh.dk/2018/12/10/danish-cpr-numbers-in-f/, sourced from the Danish Wikipedia CPR-nummer article) is a clean split with no gap: year 00-57 → 2000s, year 58-99 → 1800s.

The default branch instead reuses the wrong thresholds from the 4/9 case (year < 37 / year > 58), leaving years 37-58 falling into an else { return false; } that shouldn't exist at all — every 2-digit year should map to exactly one century for these digit values.

Reproduction (validator 13.15.35, latest on npm at time of writing)

Using checksum-valid constructed CPR numbers (day=01, month=01, century digit 5, correct check digit computed with the library's own weight formula):

const v = require('validator');
console.log(v.isTaxID('0101365018', 'dk-DK')); // true  (year 36, correct)
console.log(v.isTaxID('0101375005', 'dk-DK')); // false <-- should be true (Jan 1 2037)
console.log(v.isTaxID('0101455009', 'dk-DK')); // false <-- should be true (Jan 1 2045)
console.log(v.isTaxID('0101575004', 'dk-DK')); // false <-- should be true (Jan 1 2057)
console.log(v.isTaxID('0101585018', 'dk-DK')); // false <-- should be true (Jan 1 1858)
console.log(v.isTaxID('0101595005', 'dk-DK')); // true  (year 59, correct: Jan 1 1859)
Impact

Any application using isTaxID(input, 'dk-DK') will wrongly reject legitimate CPR numbers for:

  • People born in 1858 (year=58 exactly — a case the CPR system explicitly supports; its historical documentation cites a real 1863 registrant, so 1858 is within the system's intended range) — affects historical-records validation today.
  • Anyone issued a CPR number with a birth year of 2037-2057 under century digits 5-8 — will start affecting real newly-issued numbers as those years arrive.

This is a false-negative / over-rejection bug, not a validation bypass — no security impact, just incorrect application behavior for the affected date ranges.

Suggested fix
default: // century_digit is 5, 6, 7, or 8
  if (year <= 57) {
    year = `20${year}`;
  } else {
    year = `18${year}`;
  }
  break;

This matches the documented gap-free century-digit table for 5-8 and removes the erroneous rejection branch.

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.