Repository: validatorjs/validator.js
Pull request #2142 (2023-01-04 16:52:33)

Added support for zh-HK in isIdentityCard
## Added support for zh-HK in isIdentityCard

Supports validation for HKID (Hong Kong Identity Card)

RegEx and checksum used, compatible with new HKIDs starting with 2 letters.

## Checklist

- [x] PR contains only changes related; no stray files, etc.
- [x] README updated (where applicable)
- [x] Tests written (where applicable)


codecov[bot]: # [Codecov](https://codecov.io/gh/validatorjs/validator.js/pull/2142?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs) Report
Base: **100.00**% // Head: **99.87**% // Decreases project coverage by **`-0.12%`** :warning:
> Coverage data is based on head [(`e4a4275`)](https://codecov.io/gh/validatorjs/validator.js/pull/2142?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs) compared to base [(`531dc7f`)](https://codecov.io/gh/validatorjs/validator.js/commit/531dc7f1f75613bec75c6d888b46480455e78dc7?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs).
> Patch coverage: 82.35% of modified lines in pull request are covered.

<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff             @@
##            master    #2142      +/-   ##
===========================================
- Coverage   100.00%   99.87%   -0.13%     
===========================================
  Files          104      104              
  Lines         2308     2325      +17     
  Branches       578      583       +5     
===========================================
+ Hits          2308     2322      +14     
- Misses           0        1       +1     
- Partials         0        2       +2     
```


| [Impacted Files](https://codecov.io/gh/validatorjs/validator.js/pull/2142?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs) | Coverage Δ | |
|---|---|---|
| [src/lib/isIdentityCard.js](https://codecov.io/gh/validatorjs/validator.js/pull/2142/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs#diff-c3JjL2xpYi9pc0lkZW50aXR5Q2FyZC5qcw==) | `98.47% <82.35%> (-1.53%)` | :arrow_down: |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs)

</details>

[:umbrella: View full report at Codecov](https://codecov.io/gh/validatorjs/validator.js/pull/2142?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=validatorjs).


Patch:
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 1d346de..2c6af77 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -42,7 +42,7 @@ jobs:
 
     # Initializes the CodeQL tools for scanning.
     - name: Initialize CodeQL
-      uses: github/codeql-action/init@v1
+      uses: github/codeql-action/init@v2
       with:
         languages: ${{ matrix.language }}
         # If you wish to specify custom queries, you can do so here or in a config file.
@@ -53,7 +53,7 @@ jobs:
     # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
     # If this step fails, then you should remove it and run the build manually (see below)
     - name: Autobuild
-      uses: github/codeql-action/autobuild@v1
+      uses: github/codeql-action/autobuild@v2
 
     # ℹ️ Command-line programs to run using the OS shell.
     # 📚 https://git.io/JvXDl
@@ -67,4 +67,4 @@ jobs:
     #   make release
 
     - name: Perform CodeQL Analysis
-      uses: github/codeql-action/analyze@v1
+      uses: github/codeql-action/analyze@v2
diff --git a/README.md b/README.md
index 9372230..42e7c23 100644
--- a/README.md
+++ b/README.md
@@ -121,7 +121,7 @@ Validator                               | Description
 **isHexColor(str)**                     | check if the string is a hexadecimal color.
 **isHSL(str)**                          | check if the string is an HSL (hue, saturation, lightness, optional alpha) color based on [CSS Colors Level 4 specification](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value).<br/><br/>Comma-separated format supported. Space-separated format supported with the exception of a few edge cases (ex: `hsl(200grad+.1%62%/1)`).
 **isIBAN(str)**                         | check if a string is a IBAN (International Bank Account Number).
-**isIdentityCard(str [, locale])**      | check if the string is a valid identity card code.<br/><br/>`locale` is one of `['LK', 'PL', 'ES', 'FI', 'IN', 'IT', 'IR', 'MZ', 'NO', 'TH', 'zh-TW', 'he-IL', 'ar-LY', 'ar-TN', 'zh-CN']` OR `'any'`. If 'any' is used, function will check if any of the locals match.<br/><br/>Defaults to 'any'.
+**isIdentityCard(str [, locale])**      | check if the string is a valid identity card code.<br/><br/>`locale` is one of `['LK', 'PL', 'ES', 'FI', 'IN', 'IT', 'IR', 'MZ', 'NO', 'TH', 'zh-TW', 'he-IL', 'ar-LY', 'ar-TN', 'zh-CN', 'zh-HK']` OR `'any'`. If 'any' is used, function will check if any of the locals match.<br/><br/>Defaults to 'any'.
 **isIMEI(str [, options]))**                         | check if the string is a valid IMEI number. Imei should be of format `###############` or `##-######-######-#`.<br/><br/>`options` is an object which can contain the keys `allow_hyphens`. Defaults to first format . If allow_hyphens is set to true, the validator will validate the second format.
 **isIn(str, values)**                   | check if the string is in a array of allowed values.
 **isInt(str [, options])**              | check if the string is an integer.<br/><br/>`options` is an object which can contain the keys `min` and/or `max` to check the integer is within boundaries (e.g. `{ min: 10, max: 99 }`). `options` can also contain the key `allow_leading_zeroes`, which when set to false will disallow integer values with leading zeroes (e.g. `{ allow_leading_zeroes: false }`). Finally, `options` can contain the keys `gt` and/or `lt` which will enforce integers being greater than or less than, respectively, the value provided (e.g. `{gt: 1, lt: 4}` for a number between 1 and 4).
diff --git a/src/lib/isIdentityCard.js b/src/lib/isIdentityCard.js
index d34ddae..4734b7b 100644
--- a/src/lib/isIdentityCard.js
+++ b/src/lib/isIdentityCard.js
@@ -342,6 +342,37 @@ const validators = {
     };
     return checkIdCardNo(str);
   },
+  'zh-HK': (str) => {
+    // sanitize user input
+    str = str.trim();
+
+    // HKID number starts with 1 or 2 letters, followed by 6 digits,
+    // then a checksum contained in square / round brackets or nothing
+    const regexHKID = /^[A-Z]{1,2}[0-9]{6}((\([0-9A]\))|(\[[0-9A]\])|([0-9A]))$/;
+    const regexIsDigit = /^[0-9]$/;
+
+    // convert the user input to all uppercase and apply regex
+    str = str.toUpperCase();
+    if (!regexHKID.test(str)) return false;
+    str = str.replace(/\[|\]|\(|\)/g, '');
+
+    if (str.length === 8) str = `3${str}`;
+    let checkSumVal = 0;
+    for (let i = 0; i <= 7; i++) {
+      let convertedChar;
+      if (!regexIsDigit.test(str[i])) convertedChar = (str[i].charCodeAt(0) - 55) % 11;
+      else convertedChar = str[i];
+      checkSumVal += (convertedChar * (9 - i));
+    }
+    checkSumVal %= 11;
+
+    let checkSumConverted;
+    if (checkSumVal === 0) checkSumConverted = '0';
+    else if (checkSumVal === 1) checkSumConverted = 'A';
+    else checkSumConverted = String(11 - checkSumVal);
+    if (checkSumConverted === str[str.length - 1]) return true;
+    return false;
+  },
   'zh-TW': (str) => {
     const ALPHABET_CODES = {
       A: 10,


Test patch:
diff --git a/test/validators.js b/test/validators.js
index c0e36be..d270f0f 100644
--- a/test/validators.js
+++ b/test/validators.js
@@ -5518,6 +5518,29 @@ describe('Validators', () => {
 
   it('should validate identity cards', () => {
     const fixtures = [
+      {
+        locale: 'zh-HK',
+        valid: [
+          'OV290326[A]',
+          'Q803337[0]',
+          'Z0977986',
+          'W520128(7)',
+          'A494866[4]',
+          'A494866(4)',
+          'Z867821A',
+          'ag293013(9)',
+          'k348609(5)',
+        ],
+        invalid: [
+          'A1234567890',
+          '98765432',
+          'O962472(9)',
+          'M4578601',
+          'X731324[8]',
+          'C503134(5)',
+          'RH265886(3)',
+        ],
+      },
       {
         locale: 'LK',
         valid: [