From 3645a84c1a8030e57b65e87609f85d84e17a6dce Mon Sep 17 00:00:00 2001 From: Steven C Date: Mon, 10 Nov 2025 16:52:49 -0500 Subject: [PATCH 1/2] Update SWIFT PII detection --- src/__tests__/unit/checks/pii.test.ts | 41 ++++++++++++- src/checks/pii.ts | 88 ++++++++++++++++++++++++++- 2 files changed, 125 insertions(+), 4 deletions(-) diff --git a/src/__tests__/unit/checks/pii.test.ts b/src/__tests__/unit/checks/pii.test.ts index b63abf0..24ec246 100644 --- a/src/__tests__/unit/checks/pii.test.ts +++ b/src/__tests__/unit/checks/pii.test.ts @@ -224,19 +224,54 @@ describe('pii guardrail', () => { expect(result.info?.checked_text).toBe('cvv='); }); - it('detects BIC/SWIFT codes', async () => { + it('detects BIC/SWIFT codes with explicit prefixes', async () => { const config = PIIConfig.parse({ entities: [PIIEntity.BIC_SWIFT], block: false, }); - const text = 'Transfer to BIC DEXXDEXX tomorrow.'; + const text = 'Transfer to BIC DEUTDEFF500 tomorrow.'; const result = await pii({}, text, config); - expect((result.info?.detected_entities as Record)?.BIC_SWIFT).toEqual(['DEXXDEXX']); + expect((result.info?.detected_entities as Record)?.BIC_SWIFT).toEqual([ + 'DEUTDEFF500', + ]); expect(result.info?.checked_text).toBe('Transfer to BIC tomorrow.'); }); + it('detects BIC/SWIFT codes from known bank prefixes', async () => { + const config = PIIConfig.parse({ + entities: [PIIEntity.BIC_SWIFT], + block: false, + }); + const text = 'Send funds to CHASUS33 by Friday.'; + + const result = await pii({}, text, config); + + expect((result.info?.detected_entities as Record)?.BIC_SWIFT).toEqual(['CHASUS33']); + expect(result.info?.checked_text).toBe('Send funds to by Friday.'); + }); + + it('does not flag common words as BIC/SWIFT codes', async () => { + const config = PIIConfig.parse({ + entities: [PIIEntity.BIC_SWIFT], + block: false, + }); + const texts = [ + 'The CUSTOMER ordered a product.', + 'We will REGISTER your account.', + 'Please CONSIDER this option.', + 'The DOCUMENT is ready.', + 'This is ABSTRACT art.', + ]; + + for (const text of texts) { + const result = await pii({}, text, config); + expect((result.info?.detected_entities as Record)?.BIC_SWIFT).toBeUndefined(); + expect(result.info?.pii_detected).toBe(false); + } + }); + it('detects precise street addresses as location', async () => { const config = PIIConfig.parse({ entities: [PIIEntity.LOCATION], diff --git a/src/checks/pii.ts b/src/checks/pii.ts index e91273b..582b161 100644 --- a/src/checks/pii.ts +++ b/src/checks/pii.ts @@ -205,6 +205,89 @@ interface PiiDetectionResult { spans: ReplacementSpan[]; } +const BIC_CONTEXT_PREFIX_PATTERN = [ + '(?:[sS][wW][iI][fF][tT])', + '(?:[bB][iI][cC])', + '(?:[bB][aA][nN][kK][\\s-]?[cC][oO][dD][eE])', + '(?:[sS][wW][iI][fF][tT][\\s-]?[cC][oO][dD][eE])', + '(?:[bB][iI][cC][\\s-]?[cC][oO][dD][eE])', +].join('|'); + +const BIC_WITH_CONTEXT_REGEX = new RegExp( + `(?:${BIC_CONTEXT_PREFIX_PATTERN})[:\\s=]+([A-Z]{4}[A-Z]{2}[A-Z0-9]{2}(?:[A-Z0-9]{3})?)\\b`, + 'g' +); + +const KNOWN_BIC_PREFIXES = [ + 'DEUT', + 'CHAS', + 'BARC', + 'HSBC', + 'BNPA', + 'CITI', + 'WELL', + 'BOFA', + 'JPMC', + 'GSCC', + 'MSNY', + 'COBA', + 'DRSD', + 'BYLADEM', + 'MALADE', + 'HYVEDEMM', + 'WFBI', + 'USBC', + 'LOYD', + 'MIDL', + 'NWBK', + 'RBOS', + 'CRLY', + 'SOGEFRPP', + 'AGRIFRPP', + 'UBSW', + 'CRESCHZZ', + 'SANB', + 'BBVA', + 'UNCRITMM', + 'BCITITMMXXX', + 'INGB', + 'ABNA', + 'RABO', + 'ROYA', + 'TDOM', + 'BNSC', + 'ANZB', + 'NATA', + 'WPAC', + 'CTBA', + 'BKCHJPJT', + 'MHCBJPJT', + 'BOTKJPJT', + 'ICBKCNBJ', + 'BKCHCNBJ', + 'ABOCCNBJ', + 'PCBCCNBJ', + 'HSBCHKHH', + 'SCBLHKHH', + 'DBSSSGSG', + 'OCBCSGSG', + 'UOVBSGSG', + 'CZNB', + 'SHBK', + 'KOEX', + 'HVBK', + 'NACF', + 'IBKO', + 'KODB', + 'HNBN', + 'CITIKRSX', +]; + +const KNOWN_BIC_REGEX = new RegExp( + `\\b(?:${KNOWN_BIC_PREFIXES.join('|')})[A-Z]{2}[A-Z0-9]{2}(?:[A-Z0-9]{3})?\\b`, + 'g' +); + /** * Default regex patterns for PII entity types. */ @@ -245,7 +328,10 @@ const DEFAULT_PII_PATTERNS: Record = { group: 1, }, ], - [PIIEntity.BIC_SWIFT]: [{ regex: /\b[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}(?:[A-Z0-9]{3})?\b/g }], + [PIIEntity.BIC_SWIFT]: [ + { regex: BIC_WITH_CONTEXT_REGEX, group: 1 }, + { regex: KNOWN_BIC_REGEX }, + ], // USA [PIIEntity.US_BANK_NUMBER]: [{ regex: /\b\d{8,17}\b/g }], From 7d2b6049b8e2f4fdbb6703703cc33e73d4085228 Mon Sep 17 00:00:00 2001 From: Steven C Date: Mon, 10 Nov 2025 17:11:15 -0500 Subject: [PATCH 2/2] Fix bank codes --- src/checks/pii.ts | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/src/checks/pii.ts b/src/checks/pii.ts index 582b161..5527d63 100644 --- a/src/checks/pii.ts +++ b/src/checks/pii.ts @@ -232,9 +232,9 @@ const KNOWN_BIC_PREFIXES = [ 'MSNY', 'COBA', 'DRSD', - 'BYLADEM', - 'MALADE', - 'HYVEDEMM', + 'BYLA', + 'MALA', + 'HYVE', 'WFBI', 'USBC', 'LOYD', @@ -242,14 +242,14 @@ const KNOWN_BIC_PREFIXES = [ 'NWBK', 'RBOS', 'CRLY', - 'SOGEFRPP', - 'AGRIFRPP', + 'SOGE', + 'AGRI', 'UBSW', - 'CRESCHZZ', + 'CRES', 'SANB', 'BBVA', - 'UNCRITMM', - 'BCITITMMXXX', + 'UNCR', + 'BCIT', 'INGB', 'ABNA', 'RABO', @@ -260,18 +260,17 @@ const KNOWN_BIC_PREFIXES = [ 'NATA', 'WPAC', 'CTBA', - 'BKCHJPJT', - 'MHCBJPJT', - 'BOTKJPJT', - 'ICBKCNBJ', - 'BKCHCNBJ', - 'ABOCCNBJ', - 'PCBCCNBJ', - 'HSBCHKHH', - 'SCBLHKHH', - 'DBSSSGSG', - 'OCBCSGSG', - 'UOVBSGSG', + 'BKCH', + 'MHCB', + 'BOTK', + 'ICBK', + 'ABOC', + 'PCBC', + 'HSBC', + 'SCBL', + 'DBSS', + 'OCBC', + 'UOVB', 'CZNB', 'SHBK', 'KOEX', @@ -280,7 +279,7 @@ const KNOWN_BIC_PREFIXES = [ 'IBKO', 'KODB', 'HNBN', - 'CITIKRSX', + 'CITI', ]; const KNOWN_BIC_REGEX = new RegExp(