Spaces:
Sleeping
Sleeping
File size: 6,566 Bytes
b6ecafa | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | /**
* 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)
})
})
|