Skip to content

Commit 3645a84

Browse files
committed
Update SWIFT PII detection
1 parent 54e5806 commit 3645a84

File tree

2 files changed

+125
-4
lines changed

2 files changed

+125
-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: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,89 @@ 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+
'BYLADEM',
236+
'MALADE',
237+
'HYVEDEMM',
238+
'WFBI',
239+
'USBC',
240+
'LOYD',
241+
'MIDL',
242+
'NWBK',
243+
'RBOS',
244+
'CRLY',
245+
'SOGEFRPP',
246+
'AGRIFRPP',
247+
'UBSW',
248+
'CRESCHZZ',
249+
'SANB',
250+
'BBVA',
251+
'UNCRITMM',
252+
'BCITITMMXXX',
253+
'INGB',
254+
'ABNA',
255+
'RABO',
256+
'ROYA',
257+
'TDOM',
258+
'BNSC',
259+
'ANZB',
260+
'NATA',
261+
'WPAC',
262+
'CTBA',
263+
'BKCHJPJT',
264+
'MHCBJPJT',
265+
'BOTKJPJT',
266+
'ICBKCNBJ',
267+
'BKCHCNBJ',
268+
'ABOCCNBJ',
269+
'PCBCCNBJ',
270+
'HSBCHKHH',
271+
'SCBLHKHH',
272+
'DBSSSGSG',
273+
'OCBCSGSG',
274+
'UOVBSGSG',
275+
'CZNB',
276+
'SHBK',
277+
'KOEX',
278+
'HVBK',
279+
'NACF',
280+
'IBKO',
281+
'KODB',
282+
'HNBN',
283+
'CITIKRSX',
284+
];
285+
286+
const KNOWN_BIC_REGEX = new RegExp(
287+
`\\b(?:${KNOWN_BIC_PREFIXES.join('|')})[A-Z]{2}[A-Z0-9]{2}(?:[A-Z0-9]{3})?\\b`,
288+
'g'
289+
);
290+
208291
/**
209292
* Default regex patterns for PII entity types.
210293
*/
@@ -245,7 +328,10 @@ const DEFAULT_PII_PATTERNS: Record<PIIEntity, PatternDefinition[]> = {
245328
group: 1,
246329
},
247330
],
248-
[PIIEntity.BIC_SWIFT]: [{ regex: /\b[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}(?:[A-Z0-9]{3})?\b/g }],
331+
[PIIEntity.BIC_SWIFT]: [
332+
{ regex: BIC_WITH_CONTEXT_REGEX, group: 1 },
333+
{ regex: KNOWN_BIC_REGEX },
334+
],
249335

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

0 commit comments

Comments
 (0)