Skip to content

Find Gulf identifiers

Presidio has no recognizer for any Gulf state. The pii detector does, built in and on by default: Saudi Arabia, the UAE, Qatar, Kuwait, Bahrain and Oman, with nothing to load and nothing to maintain. This page says what is there, what it costs, and how to hold it to identifiers that are real.

Entity What Checked by
SA_NATIONAL_ID Saudi national id (1…) and Iqama (2…), ten digits Luhn
SA_UNIFIED_NUMBER Saudi establishment number, 7… context
SA_VAT_NUMBER ZATCA tax number, 3…00003 shape
AE_EMIRATES_ID Emirates ID, 784-YYYY-NNNNNNN-C shape
AE_TRN UAE tax registration number, 100… context
AE_PASSPORT UAE passport, nine characters context
QA_ID Qatar ID, eleven digits context
KW_CIVIL_ID Kuwait civil id, twelve digits weighted sum mod 11
IBAN_CODE Omani IBAN, not yet in Presidio’s map ISO 13616
PHONE_NUMBER numbers of all six states libphonenumber

Every one of them is found in Arabic-Indic digits as readily as in ASCII. ١٠٤٤٨٨٠١٣٤ is the same identifier as 1044880134, and the fpe operator writes a replacement back in the script it found. The coverage page lists the evidence behind each row: which published validator the checksum came from, or why there is none. The Emirates ID has no validator on purpose. A Luhn over all fifteen digits is widely used, real cards are documented failing it, and a wrong validator drops the id it should have found.

Nothing needs enabling. A rule addresses the entities like any other:

rules:
- name: ids stay home
action: redact
operator: fpe
select: 'a.entity_type in ["SA_NATIONAL_ID", "AE_EMIRATES_ID", "QA_ID", "KW_CIVIL_ID"]'

and entities: on the detector narrows the set, as for any built-in.

Mobile and landline numbers are not patterns. The built-in PhoneRecognizer reads them through libphonenumber, and its default regions include all six states, so 0501234567 is a Saudi or Emirati mobile and 3312 3456 a Qatari one with nothing configured. A number written with its country code, +966 50 123 4567, is found whatever the regions are.

The default list is fourteen regions, Presidio’s eight and the six here. Each region is a library call per run of digits long enough to be one of its numbers. On ordinary prose the cost is nothing. A year or a time is shorter than any region’s shortest number and is never shown to the library. On text dense with long numbers it is the larger part of the scan, and a deployment that knows its numbering plans names them:

guardrails:
detectors:
- type: pii
phone_regions: [SA, AE]

That finds the same Saudi and Emirati numbers at a fraction of the cost.

The examples each recognizer is held to are synthetic. They satisfy each scheme’s checksum and belong to nobody, so they can ship. That also makes them proof of consistency, not of truth. None of the six issuers publishes its algorithm, and the checksums are the ones the published validators agree on.

Only you can run the check against reality, and it needs no change to the built-in. Copy the recognizer’s entry from guardrails/pii/native/gulf.yaml onto your pii detector as a custom recognizer under a name of your own, add examples with identifiers you are entitled to use, and run pistra check on that configuration:

custom_recognizers:
- name: sa_id_check
entity: SA_NATIONAL_ID
patterns:
- name: national id or iqama
regex: '\b[12]\d{9}\b'
score: 0.3
validator:
kind: luhn
examples:
- text: "my own id 10XXXXXXXX"
finds: ["10XXXXXXXX"]

ok means the real identifier holds under the same check the built-in applies. If it does not, the comment above the entry says which source the check came from. Correct that source, and ask the issuer. Drop the copy afterwards. The built-in was what you were testing.

  • Bahrain’s CPR: its check digit could not be verified from any source.
  • Saudi and Qatari passport numbers: no published format to state.
  • Driving licences: the Saudi number is the national id, found already. UAE numbers differ by emirate and have no published shape.
  • Names, addresses and employers in Arabic. Those have no format. They come from the nlp or remote detector, and a model that reads Arabic is a choice made there.