Skip to content

Commit 0af5714

Browse files
authored
Update SWIFT PII detection (#37)
* Update SWIFT PII detection * Fix bank codes
1 parent 44ac88d commit 0af5714

File tree

2 files changed

+124
-4
lines changed

2 files changed

+124
-4
lines changed

src/__tests__/unit/checks/pii.test.ts

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -224,19 +224,54 @@ describe('pii guardrail', () => {
224224
expect(result.info?.checked_text).toBe('cvv=<CVV>');
225225
});
226226

227-
it('detects BIC/SWIFT codes', async () => {
227+
it('detects BIC/SWIFT codes with explicit prefixes', async () => {
228228
const config = PIIConfig.parse({
229229
entities: [PIIEntity.BIC_SWIFT],
230230
block: false,
231231
});
232-
const text = 'Transfer to BIC DEXXDEXX tomorrow.';
232+
const text = 'Transfer to BIC DEUTDEFF500 tomorrow.';
233233

234234
const result = await pii({}, text, config);
235235

236-
expect((result.info?.detected_entities as Record<string, string[]>)?.BIC_SWIFT).toEqual(['DEXXDEXX']);
236+
expect((result.info?.detected_entities as Record<string, string[]>)?.BIC_SWIFT).toEqual([
237+
'DEUTDEFF500',
238+
]);
237239
expect(result.info?.checked_text).toBe('Transfer to BIC <BIC_SWIFT> tomorrow.');
238240
});
239241

242+
it('detects BIC/SWIFT codes from known bank prefixes', async () => {
243+
const config = PIIConfig.parse({
244+
entities: [PIIEntity.BIC_SWIFT],
245+
block: false,
246+
});
247+
const text = 'Send funds to CHASUS33 by Friday.';
248+
249+
const result = await pii({}, text, config);
250+
251+
expect((result.info?.detected_entities as Record<string, string[]>)?.BIC_SWIFT).toEqual(['CHASUS33']);
252+
expect(result.info?.checked_text).toBe('Send funds to <BIC_SWIFT> by Friday.');
253+
});
254+
255+
it('does not flag common words as BIC/SWIFT codes', async () => {
256+
const config = PIIConfig.parse({
257+
entities: [PIIEntity.BIC_SWIFT],
258+
block: false,
259+
});
260+
const texts = [
261+
'The CUSTOMER ordered a product.',
262+
'We will REGISTER your account.',
263+
'Please CONSIDER this option.',
264+
'The DOCUMENT is ready.',
265+
'This is ABSTRACT art.',
266+
];
267+
268+
for (const text of texts) {
269+
const result = await pii({}, text, config);
270+
expect((result.info?.detected_entities as Record<string, string[]>)?.BIC_SWIFT).toBeUndefined();
271+
expect(result.info?.pii_detected).toBe(false);
272+
}
273+
});
274+
240275
it('detects precise street addresses as location', async () => {
241276
const config = PIIConfig.parse({
242277
entities: [PIIEntity.LOCATION],

src/checks/pii.ts

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,88 @@ interface PiiDetectionResult {
205205
spans: ReplacementSpan[];
206206
}
207207

208+
const BIC_CONTEXT_PREFIX_PATTERN = [
209+
'(?:[sS][wW][iI][fF][tT])',
210+
'(?:[bB][iI][cC])',
211+
'(?:[bB][aA][nN][kK][\\s-]?[cC][oO][dD][eE])',
212+
'(?:[sS][wW][iI][fF][tT][\\s-]?[cC][oO][dD][eE])',
213+
'(?:[bB][iI][cC][\\s-]?[cC][oO][dD][eE])',
214+
].join('|');
215+
216+
const BIC_WITH_CONTEXT_REGEX = new RegExp(
217+
`(?:${BIC_CONTEXT_PREFIX_PATTERN})[:\\s=]+([A-Z]{4}[A-Z]{2}[A-Z0-9]{2}(?:[A-Z0-9]{3})?)\\b`,
218+
'g'
219+
);
220+
221+
const KNOWN_BIC_PREFIXES = [
222+
'DEUT',
223+
'CHAS',
224+
'BARC',
225+
'HSBC',
226+
'BNPA',
227+
'CITI',
228+
'WELL',
229+
'BOFA',
230+
'JPMC',
231+
'GSCC',
232+
'MSNY',
233+
'COBA',
234+
'DRSD',
235+
'BYLA',
236+
'MALA',
237+
'HYVE',
238+
'WFBI',
239+
'USBC',
240+
'LOYD',
241+
'MIDL',
242+
'NWBK',
243+
'RBOS',
244+
'CRLY',
245+
'SOGE',
246+
'AGRI',
247+
'UBSW',
248+
'CRES',
249+
'SANB',
250+
'BBVA',
251+
'UNCR',
252+
'BCIT',
253+
'INGB',
254+
'ABNA',
255+
'RABO',
256+
'ROYA',
257+
'TDOM',
258+
'BNSC',
259+
'ANZB',
260+
'NATA',
261+
'WPAC',
262+
'CTBA',
263+
'BKCH',
264+
'MHCB',
265+
'BOTK',
266+
'ICBK',
267+
'ABOC',
268+
'PCBC',
269+
'HSBC',
270+
'SCBL',
271+
'DBSS',
272+
'OCBC',
273+
'UOVB',
274+
'CZNB',
275+
'SHBK',
276+
'KOEX',
277+
'HVBK',
278+
'NACF',
279+
'IBKO',
280+
'KODB',
281+
'HNBN',
282+
'CITI',
283+
];
284+
285+
const KNOWN_BIC_REGEX = new RegExp(
286+
`\\b(?:${KNOWN_BIC_PREFIXES.join('|')})[A-Z]{2}[A-Z0-9]{2}(?:[A-Z0-9]{3})?\\b`,
287+
'g'
288+
);
289+
208290
/**
209291
* Default regex patterns for PII entity types.
210292
*/
@@ -245,7 +327,10 @@ const DEFAULT_PII_PATTERNS: Record<PIIEntity, PatternDefinition[]> = {
245327
group: 1,
246328
},
247329
],
248-
[PIIEntity.BIC_SWIFT]: [{ regex: /\b[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}(?:[A-Z0-9]{3})?\b/g }],
330+
[PIIEntity.BIC_SWIFT]: [
331+
{ regex: BIC_WITH_CONTEXT_REGEX, group: 1 },
332+
{ regex: KNOWN_BIC_REGEX },
333+
],
249334

250335
// USA
251336
[PIIEntity.US_BANK_NUMBER]: [{ regex: /\b\d{8,17}\b/g }],

0 commit comments

Comments
 (0)