UNPKG

sql-talk

Version:

SQL Talk - 自然言語をSQLに変換するMCPサーバー(安全性保護・SSHトンネル対応) / SQL Talk - MCP Server for Natural Language to SQL conversion with safety guards and SSH tunnel support

133 lines (107 loc) 4.5 kB
import { describe, it, expect, beforeEach } from 'vitest'; import { PIIMasker } from '@/security/pii-masker.js'; describe('PIIMasker', () => { let masker: PIIMasker; beforeEach(() => { masker = new PIIMasker(); }); describe('PII column detection', () => { it('should detect common PII column names', () => { const piiColumns = ['name', 'email', 'phone', 'address', '氏名', '住所']; const nonPiiColumns = ['id', 'created_at', 'status', 'count']; piiColumns.forEach(col => { expect(masker.isPIIColumn(col)).toBe(true); }); nonPiiColumns.forEach(col => { expect(masker.isPIIColumn(col)).toBe(false); }); }); it('should handle mixed case and patterns', () => { expect(masker.isPIIColumn('user_name')).toBe(true); expect(masker.isPIIColumn('EMAIL_ADDRESS')).toBe(true); expect(masker.isPIIColumn('phone_number')).toBe(true); }); }); describe('Data masking', () => { it('should mask PII data in query results', () => { const columns = ['id', 'name', 'email', 'status']; const rows = [ { id: 1, name: 'John Doe', email: 'john@example.com', status: 'active' }, { id: 2, name: 'Jane Smith', email: 'jane@example.com', status: 'inactive' } ]; const result = masker.maskQueryResults(columns, rows); expect(result.piiDetected).toBe(true); expect(result.maskedColumns).toContain('name'); expect(result.maskedColumns).toContain('email'); expect(result.maskedColumns).not.toContain('id'); expect(result.maskedColumns).not.toContain('status'); // Check that PII data is actually masked result.maskedRows.forEach(row => { expect(row.name).not.toBe('John Doe'); expect(row.name).not.toBe('Jane Smith'); expect(row.email).not.toBe('john@example.com'); expect(row.email).not.toBe('jane@example.com'); // Non-PII data should remain unchanged expect([1, 2]).toContain(row.id); expect(['active', 'inactive']).toContain(row.status); }); }); it('should return original data when no PII detected', () => { const columns = ['id', 'created_at', 'status']; const rows = [ { id: 1, created_at: '2024-01-01', status: 'active' } ]; const result = masker.maskQueryResults(columns, rows); expect(result.piiDetected).toBe(false); expect(result.maskedColumns).toHaveLength(0); expect(result.maskedRows).toEqual(rows); }); it('should handle null and undefined values', () => { const columns = ['name']; const rows = [ { name: null }, { name: undefined }, { name: 'John Doe' } ]; const result = masker.maskQueryResults(columns, rows); expect(result.maskedRows[0].name).toBeNull(); expect(result.maskedRows[1].name).toBeUndefined(); expect(result.maskedRows[2].name).not.toBe('John Doe'); }); }); describe('SQL PII detection', () => { it('should detect PII columns in SQL queries', () => { const sql = 'SELECT id, name, email, phone FROM users WHERE active = true'; const result = masker.detectPIIInSQL(sql); expect(result.hasPII).toBe(true); expect(result.piiColumns).toContain('name'); expect(result.piiColumns).toContain('email'); expect(result.piiColumns).toContain('phone'); expect(result.warnings).toHaveLength(3); }); it('should handle complex SQL with aliases', () => { const sql = 'SELECT u.id, u.name as user_name, u.email FROM users u'; const result = masker.detectPIIInSQL(sql); expect(result.hasPII).toBe(true); expect(result.piiColumns).toContain('name'); expect(result.piiColumns).toContain('email'); }); it('should return no PII for safe queries', () => { const sql = 'SELECT id, created_at, status FROM orders'; const result = masker.detectPIIInSQL(sql); expect(result.hasPII).toBe(false); expect(result.piiColumns).toHaveLength(0); expect(result.warnings).toHaveLength(0); }); }); describe('Masking strategies', () => { it('should support different masking strategies', () => { masker.setStrategy('hash'); expect(masker.getStrategy()).toBe('hash'); masker.setStrategy('redact'); expect(masker.getStrategy()).toBe('redact'); masker.setStrategy('partial'); expect(masker.getStrategy()).toBe('partial'); }); }); });