service.test.ts•2.83 kB
import { describe, test, expect } from 'vitest'
import { SecurityService } from '../service.js'
describe('SecurityService', () => {
  const svc = new SecurityService()
  describe('validateIdentifier', () => {
    test('accepts safe identifiers', () => {
      expect(() => svc.validateIdentifier('users')).not.toThrow()
      expect(() => svc.validateIdentifier('db.users_01')).not.toThrow()
      expect(() => svc.validateIdentifier('schema$1.table_2')).not.toThrow()
    })
    test('rejects unsafe identifiers', () => {
      expect(() => svc.validateIdentifier('users;drop')).toThrow()
      expect(() => svc.validateIdentifier('users-1')).toThrow()
      expect(() => svc.validateIdentifier('users name')).toThrow()
    })
  })
  describe('validateWhereClause', () => {
    test('accepts safe where clause', () => {
      expect(() => svc.validateWhereClause("id > 0 AND name LIKE 'A%'")).not.toThrow()
    })
    test('rejects dangerous keywords', () => {
      expect(() => svc.validateWhereClause('DROP TABLE users')).toThrow()
      expect(() => svc.validateWhereClause('1 = 1; SELECT * FROM users')).toThrow()
      expect(() => svc.validateWhereClause('name = \"a\" -- comment')).toThrow()
      expect(() => svc.validateWhereClause('name = \"a\" # inline')).toThrow()
    })
  })
  describe('validateReadOnlyQuery', () => {
    test('accepts SELECT and SHOW', () => {
      expect(() => svc.validateReadOnlyQuery('SELECT * FROM users')).not.toThrow()
      expect(() => svc.validateReadOnlyQuery('  show tables')).not.toThrow()
    })
    test('rejects non-readonly or dangerous', () => {
      expect(() => svc.validateReadOnlyQuery('UPDATE users SET a=1')).toThrow()
      expect(() => svc.validateReadOnlyQuery('DELETE FROM users')).toThrow()
      expect(() => svc.validateReadOnlyQuery('SELECT * FROM a UNION SELECT * FROM b')).toThrow()
      expect(() => svc.validateReadOnlyQuery('SELECT * FROM users; SELECT * FROM secrets')).toThrow()
    })
  })
  describe('sanitizeSampleData', () => {
    test('masks sensitive columns', () => {
      const data = {
        columns: ['id', 'password', 'name', 'api_key'],
        data: [ [1, 'p@ss', 'tom', 'xyz'], [2, 'p2', 'jerry', 'abc'] ],
        total: 2,
        hasMore: false
      }
      const res = svc.sanitizeSampleData(data)
      expect(res.data[0][1]).toBe('***REDACTED***')
      expect(res.data[0][3]).toBe('***REDACTED***')
      expect(res.data[0][2]).toBe('tom')
    })
  })
  describe('sanitizeQueryResults', () => {
    test('masks sensitive fields in rows', () => {
      const rows = [
        { id: 1, token: 't', name: 'tom' },
        { id: 2, token: 't2', name: 'jerry' }
      ]
      const res = svc.sanitizeQueryResults(rows)
      expect(res[0].token).toBe('***REDACTED***')
      expect(res[1].name).toBe('jerry')
    })
  })
})