#!/usr/bin/env node import { execSync } from 'node:child_process' import { readFileSync } from 'node:fs' const KEY_PATTERNS = [ /sk-or-v1-[A-Za-z0-9]{20,}/, /sk-[A-Za-z0-9]{32,}/, /AKIA[0-9A-Z]{16}/, /gh[pousr]_[A-Za-z0-9]{36,}/, /glpat-[A-Za-z0-9_-]{20,}/, /BEGIN PRIVATE KEY/, /xox[baprs]-[A-Za-z0-9-]{10,}/, ] const OBSIDIAN_HEX_PATTERN = /^[0-9a-f]{64}$/i let found = false const trackedFiles = execSync('git ls-files', { encoding: 'utf8' }) .split('\n') .filter(Boolean) for (const file of trackedFiles) { if (file.endsWith('.png') || file.endsWith('.jpg') || file.endsWith('.jpeg') || file.endsWith('.gif') || file.endsWith('.webp') || file.endsWith('.svg') || file.endsWith('.ico') || file.endsWith('.woff') || file.endsWith('.woff2') || file.endsWith('.ttf') || file.endsWith('.eot') || file.endsWith('.pdf') || file.endsWith('.zip') || file.endsWith('.gz') || file.endsWith('.tar') || file.endsWith('.mp4') || file.endsWith('.mp3') || file.endsWith('.wav')) { continue } let content try { content = readFileSync(file, 'utf8') } catch { continue } const lines = content.split('\n') for (let i = 0; i < lines.length; i++) { const line = lines[i] for (const pattern of KEY_PATTERNS) { if (pattern.test(line)) { console.error(`KEY LEAK: ${file}:${i + 1}`) found = true } } if (file.startsWith('.obsidian/plugins/') && file.endsWith('.json')) { const tokens = line.split(/[\s":,]+/).filter(Boolean) for (const token of tokens) { if (OBSIDIAN_HEX_PATTERN.test(token) && token.length === 64) { console.error(`OBSIDIAN HEX KEY: ${file}:${i + 1}`) found = true break } } } } } if (found) { process.exit(1) }