-
Notifications
You must be signed in to change notification settings - Fork 2.1k
feat(ingestion): add India PII patterns for Aadhaar, PAN, UPI #27237
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
5849292
c3b60a5
9f6c619
82cec35
57f6053
0c8018b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,92 @@ | ||||||
| """ | ||||||
| India specific PII patterns for DPDP Act 2023 compliance. | ||||||
| Extends OpenMetadata auto PII tagging with locale aware detection. | ||||||
| """ | ||||||
|
|
||||||
| import re | ||||||
|
|
||||||
| # Patterns for column name matching | ||||||
| INDIA_COLUMN_PATTERNS = { | ||||||
| "aadhaar": re.compile(r".*aadhaar.*|.*aadhar.*|.*uidai.*", re.IGNORECASE), | ||||||
| "pan": re.compile( | ||||||
| r".*\bpan_?(card|number|no|num)\b.*|.*permanent_account.*", | ||||||
| re.IGNORECASE, | ||||||
| ), | ||||||
| "upi": re.compile( | ||||||
| r".*\bupi_?(id|address|vpa)\b.*|.*\bvpa\b.*", | ||||||
|
||||||
| r".*\bupi_?(id|address|vpa)\b.*|.*\bvpa\b.*", | |
| r".*(?<![a-z0-9])upi_?(id|address|vpa)(?![a-z0-9]).*|.*(?<![a-z0-9])vpa(?![a-z0-9]).*", |
Copilot
AI
Apr 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This module appears to need black formatting (e.g., the Verhoeff tables aren’t spaced/indented per project formatting), which will likely cause CI formatting checks to fail. Please run the ingestion formatter (make py_format / black) on this file before merging.
Copilot
AI
Apr 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is_india_pii_column uses PEP 604 union syntax (str | None), which is invalid on Python 3.9 (the ingestion package declares requires-python >=3.9). Please switch to Optional[str] / Union[str, None] (or otherwise ensure 3.9 compatibility).
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -55,6 +55,11 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from metadata.pii.constants import PII | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from metadata.utils import fqn | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from metadata.utils.logger import profiler_logger | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from metadata.pii.india_patterns import ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| is_india_pii_column, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| validate_aadhaar, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| validate_pan, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logger = profiler_logger() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -105,6 +110,27 @@ def create_column_tag_labels( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if PII in tag.tagFQN.root: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Check India PII patterns | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| india_pii = is_india_pii_column(column.name.root) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # India PII validation - check majority of samples like existing classifier | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sample_values = [str(v) for v in sample_data if v] if sample_data else [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+113
to
+116
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if india_pii == "Aadhaar" and sample_values: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| valid_count = sum(1 for v in sample_values if validate_aadhaar(v)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if valid_count > len(sample_values) * 0.5: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return [self.build_tag_label(PIISensitivityTag.SENSITIVE, "India Aadhaar detected")] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if india_pii == "PAN" and sample_values: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| valid_count = sum(1 for v in sample_values if validate_pan(v)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if valid_count > len(sample_values) * 0.5: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return [self.build_tag_label(PIISensitivityTag.SENSITIVE, "India PAN detected")] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+121
to
+126
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if india_pii == "UPI" and sample_values: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # UPI validation is simpler - check for @ symbol in majority | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| valid_count = sum(1 for v in sample_values if "@" in v and len(v) > 5) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if valid_count > len(sample_values) * 0.5: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return [self.build_tag_label(PIISensitivityTag.SENSITIVE, "India UPI detected")] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+121
to
+132
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return [self.build_tag_label(PIISensitivityTag.SENSITIVE, "India Aadhaar detected")] | |
| if india_pii == "PAN" and sample_values: | |
| valid_count = sum(1 for v in sample_values if validate_pan(v)) | |
| if valid_count > len(sample_values) * 0.5: | |
| return [self.build_tag_label(PIISensitivityTag.SENSITIVE, "India PAN detected")] | |
| if india_pii == "UPI" and sample_values: | |
| # UPI validation is simpler - check for @ symbol in majority | |
| valid_count = sum(1 for v in sample_values if "@" in v and len(v) > 5) | |
| if valid_count > len(sample_values) * 0.5: | |
| return [self.build_tag_label(PIISensitivityTag.SENSITIVE, "India UPI detected")] | |
| return [ | |
| self.build_tag_label( | |
| PIISensitivityTag.SENSITIVE, "India Aadhaar detected" | |
| ) | |
| ] | |
| if india_pii == "PAN" and sample_values: | |
| valid_count = sum(1 for v in sample_values if validate_pan(v)) | |
| if valid_count > len(sample_values) * 0.5: | |
| return [ | |
| self.build_tag_label( | |
| PIISensitivityTag.SENSITIVE, "India PAN detected" | |
| ) | |
| ] | |
| if india_pii == "UPI" and sample_values: | |
| # UPI validation is simpler - check for @ symbol in majority | |
| valid_count = sum(1 for v in sample_values if "@" in v and len(v) > 5) | |
| if valid_count > len(sample_values) * 0.5: | |
| return [ | |
| self.build_tag_label( | |
| PIISensitivityTag.SENSITIVE, "India UPI detected" | |
| ) | |
| ] |
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,28 @@ | ||||||||||||||
| import pytest | ||||||||||||||
|
||||||||||||||
| import pytest |
Copilot
AI
Apr 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test file doesn’t appear black-formatted (e.g., inline comments need two spaces before #). Please run the standard ingestion formatter (make py_format / black) so formatting checks pass.
| assert validate_pan("abcdE1234f") is False # must be uppercase | |
| assert validate_pan("ABCD1234F") is False # too short | |
| assert validate_pan("ABCDE12345") is False # wrong format | |
| assert validate_pan("abcdE1234f") is False # must be uppercase | |
| assert validate_pan("ABCD1234F") is False # too short | |
| assert validate_pan("ABCDE12345") is False # wrong format |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PAN column-name regex uses
\bword boundaries, which don’t treat_as a boundary. As a result, common snake_case names likecustomer_panwon’t match (and the added unit test forcustomer_panwill fail). Adjust the pattern/normalization to handle underscores (and consider matching barepantokens too).