HuggingClaw-MissionControl / src /lib /__tests__ /scan-credentials.test.ts
nyk
feat(refactor): ready for manual QA after main sync (#274)
b6ecafa unverified
/**
* Tests for src/lib/secret-scanner.ts — scanForSecrets and redactSecrets
*/
import { describe, it, expect } from 'vitest'
import { scanForSecrets, redactSecrets } from '@/lib/secret-scanner'
describe('scanForSecrets', () => {
it('detects AWS access key IDs', () => {
const hits = scanForSecrets('My key is AKIAIOSFODNN7EXAMPLE')
expect(hits.length).toBeGreaterThanOrEqual(1)
expect(hits.some(h => h.type === 'aws_access_key')).toBe(true)
expect(hits[0].severity).toBe('critical')
})
it('detects AWS secret access keys', () => {
const hits = scanForSecrets('aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEYaa')
expect(hits.some(h => h.type === 'aws_secret_key')).toBe(true)
})
it('detects GitHub personal access tokens (ghp_)', () => {
const token = 'ghp_' + 'A'.repeat(36)
const hits = scanForSecrets(`token: ${token}`)
expect(hits.some(h => h.type === 'github_token')).toBe(true)
expect(hits[0].severity).toBe('critical')
})
it('detects GitHub OAuth tokens (gho_)', () => {
const token = 'gho_' + 'B'.repeat(36)
const hits = scanForSecrets(token)
expect(hits.some(h => h.type === 'github_oauth_token')).toBe(true)
})
it('detects GitHub fine-grained PATs (github_pat_)', () => {
const token = 'github_pat_' + 'C'.repeat(22)
const hits = scanForSecrets(token)
expect(hits.some(h => h.type === 'github_pat')).toBe(true)
})
it('detects Stripe live keys', () => {
const key = 'sk_live_' + 'D'.repeat(24)
const hits = scanForSecrets(key)
expect(hits.some(h => h.type === 'stripe_secret_key')).toBe(true)
expect(hits[0].severity).toBe('critical')
})
it('detects Stripe test keys with warning severity', () => {
const key = 'sk_test_' + 'E'.repeat(24)
const hits = scanForSecrets(key)
expect(hits.some(h => h.type === 'stripe_test_key')).toBe(true)
expect(hits.find(h => h.type === 'stripe_test_key')!.severity).toBe('warning')
})
it('detects JWTs', () => {
const jwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'
const hits = scanForSecrets(jwt)
expect(hits.some(h => h.type === 'jwt')).toBe(true)
})
it('detects private keys (PEM)', () => {
const hits = scanForSecrets('-----BEGIN RSA PRIVATE KEY-----\nMIIEow...')
expect(hits.some(h => h.type === 'private_key')).toBe(true)
expect(hits[0].severity).toBe('critical')
})
it('detects database connection strings', () => {
const hits = scanForSecrets('postgres://user:pass@host:5432/mydb?sslmode=require')
expect(hits.some(h => h.type === 'db_connection_string')).toBe(true)
expect(hits[0].severity).toBe('critical')
})
it('detects mongodb+srv connection strings', () => {
const hits = scanForSecrets('mongodb+srv://admin:pw123@cluster0.mongodb.net/db')
expect(hits.some(h => h.type === 'db_connection_string')).toBe(true)
})
it('returns no false positives on normal text', () => {
const hits = scanForSecrets('Hello, this is a normal message about deploying our application to production.')
expect(hits).toHaveLength(0)
})
it('returns no false positives on code snippets', () => {
const hits = scanForSecrets('const x = 42; function hello() { return "world"; }')
expect(hits).toHaveLength(0)
})
it('returns redactedPreview for each match', () => {
const hits = scanForSecrets('AKIAIOSFODNN7EXAMPLE')
expect(hits[0].redactedPreview).toContain('***')
expect(hits[0].redactedPreview).not.toBe('AKIAIOSFODNN7EXAMPLE')
})
it('includes position in match', () => {
const text = 'prefix AKIAIOSFODNN7EXAMPLE suffix'
const hits = scanForSecrets(text)
expect(hits[0].position).toBe(7)
})
})
describe('redactSecrets', () => {
it('masks AWS keys in text', () => {
const text = 'Key is AKIAIOSFODNN7EXAMPLE here'
const result = redactSecrets(text)
expect(result).toContain('***REDACTED***')
expect(result).not.toContain('AKIAIOSFODNN7EXAMPLE')
})
it('masks GitHub tokens', () => {
const token = 'ghp_' + 'A'.repeat(36)
const result = redactSecrets(`Use ${token} for auth`)
expect(result).toContain('***REDACTED***')
expect(result).not.toContain(token)
})
it('preserves text without credentials', () => {
const text = 'Just a normal message with nothing sensitive.'
expect(redactSecrets(text)).toBe(text)
})
it('masks multiple credentials in one string', () => {
const token = 'ghp_' + 'X'.repeat(36)
const text = `AWS: AKIAIOSFODNN7EXAMPLE GitHub: ${token}`
const result = redactSecrets(text)
expect(result).not.toContain('AKIAIOSFODNN7EXAMPLE')
expect(result).not.toContain(token)
})
})
describe('scanForSecrets - new patterns', () => {
it('detects Slack webhook URLs', () => {
const url = 'https://hooks.slack.com/services/T' + '0'.repeat(8) + '/B' + '0'.repeat(8) + '/' + 'X'.repeat(24)
const hits = scanForSecrets(url)
expect(hits.some(h => h.type === 'slack_webhook')).toBe(true)
expect(hits.find(h => h.type === 'slack_webhook')!.severity).toBe('critical')
})
it('detects Discord webhook URLs', () => {
const url = 'https://discord.com/api/webhooks/12345678901234567/' + 'a'.repeat(68)
const hits = scanForSecrets(url)
expect(hits.some(h => h.type === 'discord_webhook')).toBe(true)
})
it('detects Anthropic API keys', () => {
const key = 'sk-ant-api' + 'A'.repeat(30)
const hits = scanForSecrets(key)
expect(hits.some(h => h.type === 'anthropic_api_key')).toBe(true)
expect(hits[0].severity).toBe('critical')
})
it('detects SendGrid API keys', () => {
const key = 'SG.' + 'A'.repeat(22) + '.' + 'B'.repeat(43)
const hits = scanForSecrets(key)
expect(hits.some(h => h.type === 'sendgrid_api_key')).toBe(true)
})
it('detects Mailgun API keys', () => {
const key = 'key-' + 'a'.repeat(32)
const hits = scanForSecrets(key)
expect(hits.some(h => h.type === 'mailgun_api_key')).toBe(true)
})
it('detects Azure storage connection strings', () => {
const conn = 'DefaultEndpointsProtocol=https;AccountName=myaccount;AccountKey=' + 'A'.repeat(30)
const hits = scanForSecrets(conn)
expect(hits.some(h => h.type === 'azure_storage')).toBe(true)
})
it('detects Twilio API keys', () => {
const key = 'SK' + 'ab12cd34ef56ab12cd34ef56ab12cd34'
const hits = scanForSecrets(key)
expect(hits.some(h => h.type === 'twilio_api_key')).toBe(true)
})
})