cto-ai-cli 3.2.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DOCS.md +201 -0
- package/README.md +70 -2
- package/dist/action/index.js +607 -83
- package/dist/api/dashboard.js +85 -23
- package/dist/api/dashboard.js.map +1 -1
- package/dist/api/server.js +86 -24
- package/dist/api/server.js.map +1 -1
- package/dist/cli/gateway.js +2925 -0
- package/dist/cli/score.js +2656 -217
- package/dist/cli/v2/index.js +111 -49
- package/dist/cli/v2/index.js.map +1 -1
- package/dist/engine/index.d.ts +85 -1
- package/dist/engine/index.js +643 -42
- package/dist/engine/index.js.map +1 -1
- package/dist/gateway/index.d.ts +281 -0
- package/dist/gateway/index.js +2803 -0
- package/dist/gateway/index.js.map +1 -0
- package/dist/govern/index.d.ts +45 -4
- package/dist/govern/index.js +318 -33
- package/dist/govern/index.js.map +1 -1
- package/dist/interact/index.js +86 -24
- package/dist/interact/index.js.map +1 -1
- package/dist/mcp/v2.js +108 -46
- package/dist/mcp/v2.js.map +1 -1
- package/package.json +3 -2
package/dist/govern/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/govern/audit.ts","../../src/govern/secrets.ts","../../src/engine/graph-utils.ts","../../src/govern/policy.ts","../../src/govern/snapshot.ts","../../src/govern/integrity.ts"],"sourcesContent":["import { randomUUID, createHash } from 'node:crypto';\nimport { readdir, chmod } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { userInfo } from 'node:os';\nimport { homedir } from 'node:os';\nimport type { AuditEntry, AuditAction } from '../types/govern.js';\n\n// ===== AUDIT TRAIL =====\n//\n// Immutable, tamper-evident audit log for all CTO interactions.\n// Each entry is SHA-256 hashed for integrity verification.\n\nconst CTO_DIR = '.cto-ai';\nconst AUDIT_DIR = 'audit';\nconst MAX_ENTRIES_PER_FILE = 500;\n\nfunction getAuditDir(): string {\n return join(homedir(), CTO_DIR, AUDIT_DIR);\n}\n\nfunction getCurrentAuditFile(): string {\n const date = new Date().toISOString().split('T')[0].replace(/-/g, '');\n return join(getAuditDir(), `audit_${date}.json`);\n}\n\nfunction computeIntegrityHash(entry: Omit<AuditEntry, 'integrityHash'>): string {\n const payload = JSON.stringify({\n id: entry.id,\n timestamp: entry.timestamp,\n action: entry.action,\n user: entry.user,\n projectPath: entry.projectPath,\n details: entry.details,\n });\n return createHash('sha256').update(payload).digest('hex');\n}\n\n// ===== HELPERS (inline to avoid cross-layer deps) =====\n\nasync function ensureDir(dirPath: string): Promise<void> {\n const { mkdir } = await import('node:fs/promises');\n await mkdir(dirPath, { recursive: true });\n}\n\nasync function readJSON<T>(filePath: string): Promise<T | null> {\n const { readFile } = await import('node:fs/promises');\n try {\n const content = await readFile(filePath, 'utf-8');\n return JSON.parse(content) as T;\n } catch {\n return null;\n }\n}\n\nasync function writeJSON(filePath: string, data: unknown): Promise<void> {\n const { writeFile } = await import('node:fs/promises');\n await ensureDir(join(filePath, '..'));\n await writeFile(filePath, JSON.stringify(data, null, 2), 'utf-8');\n}\n\n// ===== PUBLIC API =====\n\nexport async function logAudit(\n action: AuditAction,\n projectPath: string,\n details: Record<string, unknown> = {},\n): Promise<AuditEntry> {\n const auditDir = getAuditDir();\n await ensureDir(auditDir);\n\n let currentUser: string;\n try {\n currentUser = userInfo().username;\n } catch {\n currentUser = process.env.USER ?? process.env.USERNAME ?? 'unknown';\n }\n\n const partialEntry = {\n id: randomUUID().substring(0, 12),\n timestamp: new Date(),\n action,\n user: currentUser,\n projectPath,\n details,\n };\n\n const entry: AuditEntry = {\n ...partialEntry,\n integrityHash: computeIntegrityHash(partialEntry),\n };\n\n const auditFile = getCurrentAuditFile();\n let entries = await readJSON<AuditEntry[]>(auditFile) ?? [];\n entries.push(entry);\n\n if (entries.length > MAX_ENTRIES_PER_FILE) {\n entries = entries.slice(-MAX_ENTRIES_PER_FILE);\n }\n\n await writeJSON(auditFile, entries);\n try { await chmod(auditFile, 0o600); } catch { /* ignore on Windows */ }\n\n return entry;\n}\n\nexport async function getAuditEntries(\n options: {\n projectPath?: string;\n action?: AuditAction;\n since?: Date;\n limit?: number;\n } = {},\n): Promise<AuditEntry[]> {\n const auditDir = getAuditDir();\n let files: string[];\n try {\n files = await readdir(auditDir);\n } catch {\n return [];\n }\n\n const auditFiles = files\n .filter((f) => f.startsWith('audit_') && f.endsWith('.json'))\n .sort()\n .reverse();\n\n const allEntries: AuditEntry[] = [];\n const limit = options.limit ?? 100;\n\n for (const file of auditFiles) {\n if (allEntries.length >= limit) break;\n\n const entries = await readJSON<AuditEntry[]>(join(auditDir, file));\n if (!entries) continue;\n\n for (const entry of entries.reverse()) {\n if (allEntries.length >= limit) break;\n if (options.projectPath && entry.projectPath !== options.projectPath) continue;\n if (options.action && entry.action !== options.action) continue;\n if (options.since && new Date(entry.timestamp) < options.since) continue;\n allEntries.push(entry);\n }\n }\n\n return allEntries;\n}\n\nexport function verifyAuditEntry(entry: AuditEntry): boolean {\n const { integrityHash, ...rest } = entry;\n const expected = computeIntegrityHash(rest as Omit<AuditEntry, 'integrityHash'>);\n return expected === integrityHash;\n}\n\nexport async function verifyAuditIntegrity(): Promise<{\n totalEntries: number;\n validEntries: number;\n invalidEntries: AuditEntry[];\n}> {\n const entries = await getAuditEntries({ limit: 10000 });\n const invalidEntries: AuditEntry[] = [];\n\n for (const entry of entries) {\n if (!verifyAuditEntry(entry)) {\n invalidEntries.push(entry);\n }\n }\n\n return {\n totalEntries: entries.length,\n validEntries: entries.length - invalidEntries.length,\n invalidEntries,\n };\n}\n\nexport async function purgeOldAuditEntries(retentionDays: number): Promise<number> {\n const auditDir = getAuditDir();\n let files: string[];\n try {\n files = await readdir(auditDir);\n } catch {\n return 0;\n }\n\n const cutoff = new Date();\n cutoff.setDate(cutoff.getDate() - retentionDays);\n const cutoffStr = cutoff.toISOString().split('T')[0].replace(/-/g, '');\n\n let purged = 0;\n const { unlink } = await import('node:fs/promises');\n\n for (const file of files) {\n if (!file.startsWith('audit_') || !file.endsWith('.json')) continue;\n const dateStr = file.replace('audit_', '').replace('.json', '');\n if (dateStr < cutoffStr) {\n try {\n await unlink(join(auditDir, file));\n purged++;\n } catch { /* ignore */ }\n }\n }\n\n return purged;\n}\n","import { readFile } from 'node:fs/promises';\nimport { resolve, relative } from 'node:path';\nimport type { SecretFinding, SecretType } from '../types/govern.js';\n\n// ===== SECRET DETECTION ENGINE =====\n\ninterface SecretPattern {\n type: SecretType;\n pattern: RegExp;\n severity: SecretFinding['severity'];\n description: string;\n}\n\nconst BUILTIN_PATTERNS: { type: SecretType; source: string; flags: string; severity: SecretFinding['severity']; description: string }[] = [\n // API Keys\n { type: 'api-key', source: '(?:api[_-]?key|apikey)\\\\s*[:=]\\\\s*[\\'\"]?([a-zA-Z0-9_\\\\-]{20,})[\\'\"]?', flags: 'gi', severity: 'critical', description: 'API Key' },\n { type: 'api-key', source: 'sk-[a-zA-Z0-9]{20,}', flags: 'g', severity: 'critical', description: 'OpenAI/Anthropic API Key' },\n { type: 'api-key', source: 'sk-ant-[a-zA-Z0-9\\\\-]{20,}', flags: 'g', severity: 'critical', description: 'Anthropic API Key' },\n\n // AWS\n { type: 'aws-key', source: 'AKIA[0-9A-Z]{16}', flags: 'g', severity: 'critical', description: 'AWS Access Key ID' },\n { type: 'aws-key', source: '(?:aws_secret_access_key|aws_secret)\\\\s*[:=]\\\\s*[\\'\"]?([a-zA-Z0-9/+=]{40})[\\'\"]?', flags: 'gi', severity: 'critical', description: 'AWS Secret Key' },\n\n // Private Keys\n { type: 'private-key', source: '-----BEGIN (?:RSA |EC |DSA )?PRIVATE KEY-----', flags: 'g', severity: 'critical', description: 'Private Key' },\n { type: 'private-key', source: '-----BEGIN OPENSSH PRIVATE KEY-----', flags: 'g', severity: 'critical', description: 'SSH Private Key' },\n\n // Passwords\n { type: 'password', source: '(?:password|passwd|pwd)\\\\s*[:=]\\\\s*[\\'\"]([^\\'\"]{8,})[\\'\"](?!\\\\s*\\\\{)', flags: 'gi', severity: 'high', description: 'Hardcoded Password' },\n { type: 'password', source: '(?:DB_PASSWORD|DATABASE_PASSWORD|MYSQL_PASSWORD|POSTGRES_PASSWORD)\\\\s*[:=]\\\\s*[\\'\"]?([^\\'\"{}\\\\s]{4,})[\\'\"]?', flags: 'gi', severity: 'high', description: 'Database Password' },\n\n // Tokens\n { type: 'token', source: '(?:bearer|token|auth_token|access_token|refresh_token)\\\\s*[:=]\\\\s*[\\'\"]([a-zA-Z0-9_\\\\-.]{20,})[\\'\"](?!\\\\s*\\\\{)', flags: 'gi', severity: 'high', description: 'Auth Token' },\n { type: 'token', source: 'ghp_[a-zA-Z0-9]{36}', flags: 'g', severity: 'critical', description: 'GitHub Personal Access Token' },\n { type: 'token', source: 'gho_[a-zA-Z0-9]{36}', flags: 'g', severity: 'critical', description: 'GitHub OAuth Token' },\n { type: 'token', source: 'glpat-[a-zA-Z0-9\\\\-]{20,}', flags: 'g', severity: 'critical', description: 'GitLab Personal Access Token' },\n { type: 'token', source: 'npm_[a-zA-Z0-9]{36}', flags: 'g', severity: 'high', description: 'npm Token' },\n\n // Connection strings\n { type: 'connection-string', source: '(?:mongodb(?:\\\\+srv)?|postgres(?:ql)?|mysql|redis|amqp):\\\\/\\\\/[^\\\\s\\'\"]+:[^\\\\s\\'\"]+@[^\\\\s\\'\"]+', flags: 'gi', severity: 'critical', description: 'Database Connection String' },\n { type: 'connection-string', source: '(?:DATABASE_URL|REDIS_URL|MONGODB_URI)\\\\s*[:=]\\\\s*[\\'\"]?([^\\\\s\\'\"]{10,})[\\'\"]?', flags: 'gi', severity: 'high', description: 'Database URL' },\n\n // Environment variables with secrets\n { type: 'env-variable', source: '(?:SECRET|PRIVATE|ENCRYPTION)[_-]?(?:KEY|TOKEN|PASS)\\\\s*[:=]\\\\s*[\\'\"]?([^\\\\s\\'\"]{8,})[\\'\"]?', flags: 'gi', severity: 'high', description: 'Secret Environment Variable' },\n\n // Stripe\n { type: 'api-key', source: 'sk_live_[a-zA-Z0-9]{24,}', flags: 'g', severity: 'critical', description: 'Stripe Live Secret Key' },\n { type: 'api-key', source: 'pk_live_[a-zA-Z0-9]{24,}', flags: 'g', severity: 'high', description: 'Stripe Live Publishable Key' },\n { type: 'api-key', source: 'rk_live_[a-zA-Z0-9]{24,}', flags: 'g', severity: 'critical', description: 'Stripe Restricted Key' },\n\n // Slack\n { type: 'token', source: 'xoxb-[0-9]{10,}-[0-9]{10,}-[a-zA-Z0-9]{24,}', flags: 'g', severity: 'critical', description: 'Slack Bot Token' },\n { type: 'token', source: 'xoxp-[0-9]{10,}-[0-9]{10,}-[a-zA-Z0-9]{24,}', flags: 'g', severity: 'critical', description: 'Slack User Token' },\n { type: 'api-key', source: 'https://hooks\\\\.slack\\\\.com/services/T[a-zA-Z0-9_]+/B[a-zA-Z0-9_]+/[a-zA-Z0-9_]+', flags: 'g', severity: 'high', description: 'Slack Webhook URL' },\n\n // Google\n { type: 'api-key', source: 'AIza[0-9A-Za-z_-]{35}', flags: 'g', severity: 'high', description: 'Google API Key' },\n { type: 'token', source: 'ya29\\\\.[0-9A-Za-z_-]+', flags: 'g', severity: 'high', description: 'Google OAuth Token' },\n\n // Azure\n { type: 'api-key', source: '(?:AccountKey|SharedAccessKey)\\\\s*=\\\\s*[a-zA-Z0-9+/=]{40,}', flags: 'g', severity: 'critical', description: 'Azure Storage Key' },\n\n // Twilio\n { type: 'api-key', source: 'AC[a-f0-9]{32}', flags: 'g', severity: 'high', description: 'Twilio Account SID' },\n\n // SendGrid\n { type: 'api-key', source: 'SG\\\\.[a-zA-Z0-9_-]{22}\\\\.[a-zA-Z0-9_-]{43}', flags: 'g', severity: 'critical', description: 'SendGrid API Key' },\n\n // JWT\n { type: 'token', source: 'eyJ[a-zA-Z0-9_-]{10,}\\\\.eyJ[a-zA-Z0-9_-]{10,}\\\\.[a-zA-Z0-9_-]{10,}', flags: 'g', severity: 'high', description: 'JSON Web Token' },\n\n // PII\n { type: 'pii', source: '\\\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\\\.[A-Z|a-z]{2,}\\\\b', flags: 'g', severity: 'medium', description: 'Email Address (PII)' },\n { type: 'pii', source: '\\\\b\\\\d{3}[-.]?\\\\d{2}[-.]?\\\\d{4}\\\\b', flags: 'g', severity: 'high', description: 'Possible SSN (PII)' },\n];\n\nfunction buildPatterns(customPatterns: string[] = []): SecretPattern[] {\n const patterns: SecretPattern[] = BUILTIN_PATTERNS.map((def) => ({\n type: def.type,\n pattern: new RegExp(def.source, def.flags),\n severity: def.severity,\n description: def.description,\n }));\n\n for (const custom of customPatterns) {\n try {\n patterns.push({\n type: 'custom',\n pattern: new RegExp(custom, 'gi'),\n severity: 'medium',\n description: `Custom pattern: ${custom}`,\n });\n } catch { /* skip invalid regex */ }\n }\n\n return patterns;\n}\n\nexport function scanContentForSecrets(\n content: string,\n filePath: string,\n customPatterns: string[] = [],\n): SecretFinding[] {\n const findings: SecretFinding[] = [];\n const lines = content.split('\\n');\n const allPatterns = buildPatterns(customPatterns);\n\n for (const secretPattern of allPatterns) {\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n secretPattern.pattern.lastIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = secretPattern.pattern.exec(line)) !== null) {\n const matchText = match[0];\n if (isTemplateOrPlaceholder(matchText)) continue;\n\n findings.push({\n type: secretPattern.type,\n file: filePath,\n line: i + 1,\n match: matchText,\n redacted: redactSecret(matchText),\n severity: secretPattern.severity,\n });\n }\n }\n }\n\n return deduplicateFindings(findings);\n}\n\nexport async function scanFileForSecrets(\n filePath: string,\n projectPath: string,\n customPatterns: string[] = [],\n): Promise<SecretFinding[]> {\n try {\n const content = await readFile(filePath, 'utf-8');\n const relPath = relative(resolve(projectPath), resolve(filePath));\n return scanContentForSecrets(content, relPath, customPatterns);\n } catch {\n return [];\n }\n}\n\nexport async function scanProjectForSecrets(\n projectPath: string,\n filePaths: string[],\n customPatterns: string[] = [],\n): Promise<SecretFinding[]> {\n const allFindings: SecretFinding[] = [];\n\n for (const fp of filePaths) {\n const findings = await scanFileForSecrets(fp, projectPath, customPatterns);\n allFindings.push(...findings);\n }\n\n return allFindings.sort((a, b) => {\n const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };\n return severityOrder[a.severity] - severityOrder[b.severity];\n });\n}\n\nexport function sanitizeContent(content: string, customPatterns: string[] = []): string {\n let sanitized = content;\n const allPatterns = buildPatterns(customPatterns);\n\n for (const secretPattern of allPatterns) {\n sanitized = sanitized.replace(secretPattern.pattern, (match) => {\n if (isTemplateOrPlaceholder(match)) return match;\n return redactSecret(match);\n });\n }\n\n return sanitized;\n}\n\nfunction redactSecret(value: string): string {\n if (value.length <= 8) return '***REDACTED***';\n const prefix = value.substring(0, 4);\n const suffix = value.substring(value.length - 2);\n return `${prefix}${'*'.repeat(Math.min(value.length - 6, 20))}${suffix}`;\n}\n\nfunction isTemplateOrPlaceholder(value: string): boolean {\n const placeholders = [\n /\\$\\{.*\\}/, /\\{\\{.*\\}\\}/, /%[sd]/, /<[A-Z_]+>/, /YOUR_.*_HERE/i,\n /\\bCHANGE_ME\\b/i, /\\bPLACEHOLDER\\b/i, /\\bexample\\b/i, /\\bTODO\\b/i, /xxx+/i,\n /\\breplace.?me\\b/i, /\\bdummy\\b/i, /\\btest_?key\\b/i, /\\bsample\\b/i,\n ];\n return placeholders.some((p) => p.test(value));\n}\n\nfunction deduplicateFindings(findings: SecretFinding[]): SecretFinding[] {\n const seen = new Set<string>();\n return findings.filter((f) => {\n const key = `${f.file}:${f.line}:${f.type}:${f.match}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n\n// ===== ENTROPY ANALYSIS =====\n\nfunction shannonEntropy(str: string): number {\n const freq = new Map<string, number>();\n for (const ch of str) {\n freq.set(ch, (freq.get(ch) || 0) + 1);\n }\n let entropy = 0;\n for (const count of freq.values()) {\n const p = count / str.length;\n if (p > 0) entropy -= p * Math.log2(p);\n }\n return entropy;\n}\n\nconst HIGH_ENTROPY_RE = /['\"]([a-zA-Z0-9+/=_\\-]{30,})['\"]|=\\s*['\"]?([a-zA-Z0-9+/=_\\-]{30,})['\"]?/g;\n\nconst ENTROPY_SKIP = [\n /^[a-f0-9]{32,}$/i, // hex hashes\n /^[A-Z_]{30,}$/, // all-caps constants\n /^[a-z_]{30,}$/, // all-lowercase identifiers\n /^[a-zA-Z0-9+/]+=+$/, // base64 padding\n /^[a-z]+[A-Z][a-zA-Z]+$/, // camelCase identifiers\n /sha\\d+-/i, // integrity hashes (sha256-, sha512-)\n];\n\nexport function scanContentForHighEntropy(\n content: string,\n filePath: string,\n threshold: number = 5.0,\n): SecretFinding[] {\n const findings: SecretFinding[] = [];\n const lines = content.split('\\n');\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (line.trim().startsWith('//') || line.trim().startsWith('#') || line.trim().startsWith('*')) continue;\n\n HIGH_ENTROPY_RE.lastIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = HIGH_ENTROPY_RE.exec(line)) !== null) {\n const value = match[1] || match[2];\n if (!value || value.length < 40) continue;\n if (isTemplateOrPlaceholder(value)) continue;\n if (ENTROPY_SKIP.some((p) => p.test(value))) continue;\n\n const entropy = shannonEntropy(value);\n if (entropy >= threshold) {\n findings.push({\n type: 'high-entropy',\n file: filePath,\n line: i + 1,\n match: value,\n redacted: redactSecret(value),\n severity: entropy >= 5.0 ? 'high' : 'medium',\n });\n }\n }\n }\n\n return deduplicateFindings(findings);\n}\n\n// ===== FULL PROJECT AUDIT =====\n\nexport interface AuditResult {\n findings: SecretFinding[];\n summary: {\n totalFiles: number;\n filesScanned: number;\n filesWithSecrets: number;\n totalFindings: number;\n bySeverity: { critical: number; high: number; medium: number; low: number };\n byType: Record<string, number>;\n };\n recommendations: string[];\n}\n\nexport async function auditProject(\n projectPath: string,\n filePaths: string[],\n options: { customPatterns?: string[]; entropyThreshold?: number; includePII?: boolean } = {},\n): Promise<AuditResult> {\n const { customPatterns = [], entropyThreshold = 4.5, includePII = true } = options;\n const allFindings: SecretFinding[] = [];\n const filesWithSecrets = new Set<string>();\n\n for (const fp of filePaths) {\n try {\n const content = await readFile(fp, 'utf-8');\n const relPath = relative(resolve(projectPath), resolve(fp));\n\n // Skip test files and declaration files for entropy (too many false positives)\n const isTestFile = /\\.(test|spec|mock)\\.[jt]sx?$/.test(relPath) || relPath.includes('__tests__');\n const isDtsFile = relPath.endsWith('.d.ts');\n\n // Pattern-based detection\n let findings = scanContentForSecrets(content, relPath, customPatterns);\n\n // Filter PII if not wanted\n if (!includePII) {\n findings = findings.filter((f) => f.type !== 'pii');\n }\n\n // Entropy-based detection (skip test/declaration files — too noisy)\n const entropyFindings = (isTestFile || isDtsFile)\n ? []\n : scanContentForHighEntropy(content, relPath, entropyThreshold);\n\n const combined = [...findings, ...entropyFindings];\n if (combined.length > 0) {\n filesWithSecrets.add(relPath);\n allFindings.push(...combined);\n }\n } catch {\n // skip unreadable files\n }\n }\n\n // Sort by severity\n allFindings.sort((a, b) => {\n const order = { critical: 0, high: 1, medium: 2, low: 3 };\n return order[a.severity] - order[b.severity];\n });\n\n // Build summary\n const bySeverity = { critical: 0, high: 0, medium: 0, low: 0 };\n const byType: Record<string, number> = {};\n for (const f of allFindings) {\n bySeverity[f.severity]++;\n byType[f.type] = (byType[f.type] || 0) + 1;\n }\n\n // Generate recommendations\n const recommendations: string[] = [];\n if (bySeverity.critical > 0) {\n recommendations.push('CRITICAL: Rotate all detected credentials immediately. They may already be compromised.');\n }\n if (byType['password'] > 0) {\n recommendations.push('Move passwords to environment variables or a secrets manager (AWS Secrets Manager, Vault, etc.).');\n }\n if (byType['api-key'] > 0 || byType['aws-key'] > 0) {\n recommendations.push('Use environment variables for API keys. Never commit them to source control.');\n }\n if (byType['connection-string'] > 0) {\n recommendations.push('Database connection strings should use environment variables, not hardcoded values.');\n }\n if (byType['private-key'] > 0) {\n recommendations.push('Private keys should NEVER be in source code. Use a key management service.');\n }\n if (byType['pii'] > 0) {\n recommendations.push('PII detected. Review for GDPR/CCPA compliance. Consider data anonymization.');\n }\n if (byType['high-entropy'] > 0) {\n recommendations.push('High-entropy strings detected that may be secrets. Review manually.');\n }\n if (allFindings.length > 0) {\n recommendations.push('Add a .gitignore entry for .env files if not already present.');\n recommendations.push('Run `npx cto-ai-cli --audit` regularly or add to CI pipeline.');\n }\n if (allFindings.length === 0) {\n recommendations.push('No secrets detected. Great job keeping your codebase clean!');\n }\n\n return {\n findings: allFindings,\n summary: {\n totalFiles: filePaths.length,\n filesScanned: filePaths.length,\n filesWithSecrets: filesWithSecrets.size,\n totalFindings: allFindings.length,\n bySeverity,\n byType,\n },\n recommendations,\n };\n}\n","import type { GraphEdge } from '../types/engine.js';\n\n// ===== SHARED GRAPH UTILITIES =====\n\nexport interface AdjacencyList {\n forward: Map<string, string[]>; // file → files it imports\n reverse: Map<string, string[]>; // file → files that import it\n}\n\nexport function buildAdjacencyList(edges: GraphEdge[]): AdjacencyList {\n const forward = new Map<string, string[]>();\n const reverse = new Map<string, string[]>();\n\n for (const edge of edges) {\n if (!forward.has(edge.from)) forward.set(edge.from, []);\n forward.get(edge.from)!.push(edge.to);\n\n if (!reverse.has(edge.to)) reverse.set(edge.to, []);\n reverse.get(edge.to)!.push(edge.from);\n }\n\n return { forward, reverse };\n}\n\nexport function bfsBidirectional(\n seeds: string[],\n adj: AdjacencyList,\n depth: number,\n): Set<string> {\n const result = new Set(seeds);\n let frontier = [...seeds];\n const visited = new Set<string>();\n\n for (let d = 0; d < depth; d++) {\n const nextFrontier: string[] = [];\n\n for (const node of frontier) {\n if (visited.has(node)) continue;\n visited.add(node);\n\n // Forward neighbors (imports)\n const fwd = adj.forward.get(node);\n if (fwd) {\n for (const neighbor of fwd) {\n if (!visited.has(neighbor)) {\n result.add(neighbor);\n nextFrontier.push(neighbor);\n }\n }\n }\n\n // Reverse neighbors (imported by)\n const rev = adj.reverse.get(node);\n if (rev) {\n for (const neighbor of rev) {\n if (!visited.has(neighbor)) {\n result.add(neighbor);\n nextFrontier.push(neighbor);\n }\n }\n }\n }\n\n frontier = nextFrontier;\n }\n\n return result;\n}\n\nexport function matchGlob(path: string, pattern: string): boolean {\n const regexStr = pattern\n .replace(/\\./g, '\\\\.')\n .replace(/\\*\\*/g, '§§')\n .replace(/\\*/g, '[^/]*')\n .replace(/§§/g, '.*')\n .replace(/\\?/g, '.');\n\n try {\n return new RegExp(`^${regexStr}$`).test(path);\n } catch {\n return false;\n }\n}\n","import type {\n PolicySet,\n PolicyRule,\n PolicyRuleType,\n PolicyValidation,\n PolicyViolation,\n PolicyWarning,\n} from '../types/govern.js';\nimport type { ContextSelection, AnalyzedFile } from '../types/engine.js';\nimport { matchGlob } from '../engine/graph-utils.js';\n\n// ===== POLICY ENGINE =====\n//\n// YAML-backed rule engine for controlling what context gets included/excluded.\n// Policies are evaluated after selection to validate compliance.\n\nexport const DEFAULT_POLICY: PolicySet = {\n version: '1.0',\n name: 'default',\n rules: [\n {\n id: 'no-env',\n type: 'exclude-always',\n pattern: '**/*.env*',\n reason: 'Environment files must never be sent to AI',\n enabled: true,\n },\n {\n id: 'no-secrets',\n type: 'secret-block',\n reason: 'Files with detected secrets are blocked',\n enabled: true,\n },\n {\n id: 'min-coverage',\n type: 'coverage-minimum',\n threshold: 70,\n reason: 'Warn if context coverage drops below 70%',\n enabled: true,\n },\n ],\n};\n\n// ===== VALIDATION =====\n\nexport function validateSelection(\n selection: ContextSelection,\n policies: PolicySet,\n allFiles?: AnalyzedFile[],\n): PolicyValidation {\n const violations: PolicyViolation[] = [];\n const warnings: PolicyWarning[] = [];\n\n const includedPaths = new Set(selection.files.map((f) => f.relativePath));\n\n for (const rule of policies.rules) {\n if (!rule.enabled) continue;\n\n switch (rule.type) {\n case 'exclude-always': {\n if (!rule.pattern) break;\n const violatingFiles = selection.files.filter((f) =>\n matchGlob(f.relativePath, rule.pattern!),\n );\n for (const f of violatingFiles) {\n violations.push({\n rule,\n message: `File \"${f.relativePath}\" is included but matches exclude-always pattern \"${rule.pattern}\"`,\n severity: 'error',\n });\n }\n break;\n }\n\n case 'include-always': {\n if (!rule.pattern || !allFiles) break;\n const requiredFiles = allFiles.filter((f) =>\n matchGlob(f.relativePath, rule.pattern!),\n );\n for (const f of requiredFiles) {\n if (!includedPaths.has(f.relativePath)) {\n violations.push({\n rule,\n message: `File \"${f.relativePath}\" matches include-always pattern \"${rule.pattern}\" but is not included`,\n severity: 'warning',\n });\n }\n }\n break;\n }\n\n case 'coverage-minimum': {\n const threshold = rule.threshold ?? 70;\n if (selection.coverage.score < threshold) {\n warnings.push({\n rule,\n message: `Coverage ${selection.coverage.score}% is below minimum ${threshold}%`,\n currentValue: selection.coverage.score,\n threshold,\n });\n }\n break;\n }\n\n case 'risk-maximum': {\n const threshold = rule.threshold ?? 50;\n if (selection.riskScore > threshold) {\n warnings.push({\n rule,\n message: `Exclusion risk ${selection.riskScore}/100 exceeds maximum ${threshold}`,\n currentValue: selection.riskScore,\n threshold,\n });\n }\n break;\n }\n\n case 'budget-limit': {\n if (!rule.category || !rule.threshold) break;\n const categoryFiles = selection.files.filter((f) =>\n fileMatchesCategory(f.relativePath, rule.category!),\n );\n const categoryTokens = categoryFiles.reduce((s, f) => s + f.tokens, 0);\n const categoryPercent = selection.totalTokens > 0\n ? (categoryTokens / selection.totalTokens) * 100\n : 0;\n\n if (categoryPercent > rule.threshold) {\n warnings.push({\n rule,\n message: `Category \"${rule.category}\" uses ${Math.round(categoryPercent)}% of budget (max: ${rule.threshold}%)`,\n currentValue: Math.round(categoryPercent),\n threshold: rule.threshold,\n });\n }\n break;\n }\n }\n }\n\n return {\n passed: violations.filter((v) => v.severity === 'error').length === 0,\n violations,\n warnings,\n };\n}\n\n// ===== POLICY CRUD =====\n\nexport function addRule(policies: PolicySet, rule: PolicyRule): PolicySet {\n return {\n ...policies,\n rules: [...policies.rules, rule],\n };\n}\n\nexport function removeRule(policies: PolicySet, ruleId: string): PolicySet {\n return {\n ...policies,\n rules: policies.rules.filter((r) => r.id !== ruleId),\n };\n}\n\nexport function toggleRule(policies: PolicySet, ruleId: string, enabled: boolean): PolicySet {\n return {\n ...policies,\n rules: policies.rules.map((r) =>\n r.id === ruleId ? { ...r, enabled } : r,\n ),\n };\n}\n\n// ===== HELPERS =====\n\nfunction fileMatchesCategory(path: string, category: string): boolean {\n switch (category) {\n case 'test':\n return /\\.(test|spec)\\.[jt]sx?$/.test(path) || /\\/__tests__\\//.test(path) || /\\/tests?\\//.test(path);\n case 'config':\n return /\\.(config|rc)\\.[jt]s$/.test(path) || /\\.json$/.test(path) || /\\.ya?ml$/.test(path);\n case 'docs':\n return /\\.(md|txt|rst)$/.test(path);\n case 'types':\n return /types?\\//i.test(path) || /\\.d\\.ts$/.test(path);\n default:\n return path.includes(category);\n }\n}\n","import { randomUUID, createHash } from 'node:crypto';\nimport { readFile } from 'node:fs/promises';\nimport type {\n ContextSnapshot,\n SnapshotFile,\n SnapshotVerification,\n} from '../types/govern.js';\nimport type { ContextSelection, ProjectAnalysis } from '../types/engine.js';\n\n// ===== CONTEXT SNAPSHOTS =====\n//\n// Reproducible, verifiable snapshots of context selections.\n// Used for: deterministic mode, audit evidence, before/after comparisons.\n\nexport function createSnapshot(\n name: string,\n analysis: ProjectAnalysis,\n selection: ContextSelection,\n metadata: Record<string, unknown> = {},\n): ContextSnapshot {\n const files: SnapshotFile[] = selection.files.map((f) => ({\n relativePath: f.relativePath,\n hash: hashString(`${f.relativePath}:${f.tokens}:${f.pruneLevel}`),\n tokens: f.tokens,\n pruneLevel: f.pruneLevel,\n }));\n\n const snapshotData = files\n .map((f) => `${f.relativePath}:${f.hash}:${f.pruneLevel}`)\n .sort()\n .join('|');\n\n return {\n id: randomUUID().substring(0, 8),\n name,\n createdAt: new Date(),\n hash: hashString(snapshotData),\n projectHash: analysis.hash,\n analysisHash: analysis.hash,\n selectionHash: selection.hash,\n files,\n totalTokens: selection.totalTokens,\n coverageScore: selection.coverage.score,\n riskScore: selection.riskScore,\n metadata,\n };\n}\n\nexport async function verifySnapshot(\n snapshot: ContextSnapshot,\n currentAnalysis: ProjectAnalysis,\n currentSelection: ContextSelection,\n): Promise<SnapshotVerification> {\n const currentFiles = new Map(\n currentSelection.files.map((f) => [f.relativePath, f]),\n );\n\n let filesMatched = 0;\n const filesMissing: string[] = [];\n const filesChanged: string[] = [];\n\n for (const snapFile of snapshot.files) {\n const current = currentFiles.get(snapFile.relativePath);\n\n if (!current) {\n filesMissing.push(snapFile.relativePath);\n continue;\n }\n\n const currentHash = hashString(\n `${current.relativePath}:${current.tokens}:${current.pruneLevel}`,\n );\n\n if (currentHash === snapFile.hash) {\n filesMatched++;\n } else {\n filesChanged.push(snapFile.relativePath);\n }\n }\n\n // Verify overall hash\n const currentSnapshotData = snapshot.files\n .map((f) => {\n const current = currentFiles.get(f.relativePath);\n if (!current) return `${f.relativePath}:MISSING:MISSING`;\n return `${current.relativePath}:${hashString(`${current.relativePath}:${current.tokens}:${current.pruneLevel}`)}:${current.pruneLevel}`;\n })\n .sort()\n .join('|');\n\n const currentHash = hashString(currentSnapshotData);\n const integrityOk = currentHash === snapshot.hash && filesMissing.length === 0 && filesChanged.length === 0;\n\n return {\n valid: integrityOk,\n snapshotId: snapshot.id,\n filesChecked: snapshot.files.length,\n filesMatched,\n filesMissing,\n filesChanged,\n integrityOk,\n };\n}\n\nexport function compareSnapshots(\n older: ContextSnapshot,\n newer: ContextSnapshot,\n): {\n added: string[];\n removed: string[];\n changed: string[];\n tokenDelta: number;\n coverageDelta: number;\n riskDelta: number;\n} {\n const olderFiles = new Map(older.files.map((f) => [f.relativePath, f]));\n const newerFiles = new Map(newer.files.map((f) => [f.relativePath, f]));\n\n const added: string[] = [];\n const removed: string[] = [];\n const changed: string[] = [];\n\n for (const [path, file] of newerFiles) {\n const old = olderFiles.get(path);\n if (!old) {\n added.push(path);\n } else if (old.hash !== file.hash) {\n changed.push(path);\n }\n }\n\n for (const path of olderFiles.keys()) {\n if (!newerFiles.has(path)) {\n removed.push(path);\n }\n }\n\n return {\n added,\n removed,\n changed,\n tokenDelta: newer.totalTokens - older.totalTokens,\n coverageDelta: newer.coverageScore - older.coverageScore,\n riskDelta: newer.riskScore - older.riskScore,\n };\n}\n\n// ===== HELPERS =====\n\nfunction hashString(input: string): string {\n return createHash('sha256').update(input).digest('hex').substring(0, 16);\n}\n","import { createHash } from 'node:crypto';\nimport { readFile, chmod, readdir, stat } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { IntegrityManifest, IntegrityEntry } from '../types/govern.js';\n\n// ===== INTEGRITY VERIFICATION =====\n//\n// SHA-256 hash-based integrity checking for CTO artifacts.\n// Detects tampering of snapshots, audit logs, configs, and policies.\n\nexport function hashContent(content: Buffer | string): string {\n return createHash('sha256').update(content).digest('hex');\n}\n\nexport async function hashFile(filePath: string): Promise<string | null> {\n try {\n const content = await readFile(filePath);\n return hashContent(content);\n } catch {\n return null;\n }\n}\n\nexport async function buildManifest(\n projectDir: string,\n): Promise<IntegrityManifest> {\n const entries: IntegrityEntry[] = [];\n\n async function scanDir(dir: string, type: IntegrityEntry['type']): Promise<void> {\n let files: string[];\n try {\n files = await readdir(dir);\n } catch {\n return;\n }\n\n for (const file of files) {\n const fullPath = join(dir, file);\n try {\n const fileStat = await stat(fullPath);\n if (fileStat.isFile()) {\n const hash = await hashFile(fullPath);\n if (hash) {\n entries.push({\n filePath: fullPath,\n hash,\n size: fileStat.size,\n createdAt: fileStat.mtime,\n type,\n });\n }\n }\n } catch { /* skip */ }\n }\n }\n\n // Scan known CTO directories\n await scanDir(join(projectDir, 'snapshots'), 'snapshot');\n await scanDir(join(projectDir, 'audit'), 'audit');\n await scanDir(projectDir, 'config');\n\n return {\n version: '2.0',\n createdAt: new Date(),\n entries,\n };\n}\n\nexport async function verifyManifest(\n manifest: IntegrityManifest,\n): Promise<{\n totalFiles: number;\n validFiles: number;\n invalidFiles: string[];\n missingFiles: string[];\n}> {\n const invalidFiles: string[] = [];\n const missingFiles: string[] = [];\n\n for (const entry of manifest.entries) {\n const currentHash = await hashFile(entry.filePath);\n\n if (currentHash === null) {\n missingFiles.push(entry.filePath);\n } else if (currentHash !== entry.hash) {\n invalidFiles.push(entry.filePath);\n }\n }\n\n return {\n totalFiles: manifest.entries.length,\n validFiles: manifest.entries.length - invalidFiles.length - missingFiles.length,\n invalidFiles,\n missingFiles,\n };\n}\n\nexport async function securePermissions(dirPath: string): Promise<number> {\n let count = 0;\n\n try {\n await chmod(dirPath, 0o700);\n count++;\n\n const files = await readdir(dirPath);\n for (const file of files) {\n try {\n const fullPath = join(dirPath, file);\n const fileStat = await stat(fullPath);\n if (fileStat.isFile()) {\n await chmod(fullPath, 0o600);\n count++;\n }\n } catch { /* skip */ }\n }\n } catch { /* dir might not exist */ }\n\n return count;\n}\n"],"mappings":"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","names":["readFile","randomUUID","createHash","currentHash","createHash","readFile","chmod","readdir","join"]}
|
|
1
|
+
{"version":3,"sources":["../../src/govern/audit.ts","../../src/govern/secrets.ts","../../src/engine/graph-utils.ts","../../src/govern/policy.ts","../../src/govern/snapshot.ts","../../src/govern/integrity.ts"],"sourcesContent":["import { randomUUID, createHash } from 'node:crypto';\nimport { readdir, chmod } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { userInfo } from 'node:os';\nimport { homedir } from 'node:os';\nimport type { AuditEntry, AuditAction } from '../types/govern.js';\n\n// ===== AUDIT TRAIL =====\n//\n// Immutable, tamper-evident audit log for all CTO interactions.\n// Each entry is SHA-256 hashed for integrity verification.\n\nconst CTO_DIR = '.cto-ai';\nconst AUDIT_DIR = 'audit';\nconst MAX_ENTRIES_PER_FILE = 500;\n\nfunction getAuditDir(): string {\n return join(homedir(), CTO_DIR, AUDIT_DIR);\n}\n\nfunction getCurrentAuditFile(): string {\n const date = new Date().toISOString().split('T')[0].replace(/-/g, '');\n return join(getAuditDir(), `audit_${date}.json`);\n}\n\nfunction computeIntegrityHash(entry: Omit<AuditEntry, 'integrityHash'>): string {\n const payload = JSON.stringify({\n id: entry.id,\n timestamp: entry.timestamp,\n action: entry.action,\n user: entry.user,\n projectPath: entry.projectPath,\n details: entry.details,\n });\n return createHash('sha256').update(payload).digest('hex');\n}\n\n// ===== HELPERS (inline to avoid cross-layer deps) =====\n\nasync function ensureDir(dirPath: string): Promise<void> {\n const { mkdir } = await import('node:fs/promises');\n await mkdir(dirPath, { recursive: true });\n}\n\nasync function readJSON<T>(filePath: string): Promise<T | null> {\n const { readFile } = await import('node:fs/promises');\n try {\n const content = await readFile(filePath, 'utf-8');\n return JSON.parse(content) as T;\n } catch {\n return null;\n }\n}\n\nasync function writeJSON(filePath: string, data: unknown): Promise<void> {\n const { writeFile } = await import('node:fs/promises');\n await ensureDir(join(filePath, '..'));\n await writeFile(filePath, JSON.stringify(data, null, 2), 'utf-8');\n}\n\n// ===== PUBLIC API =====\n\nexport async function logAudit(\n action: AuditAction,\n projectPath: string,\n details: Record<string, unknown> = {},\n): Promise<AuditEntry> {\n const auditDir = getAuditDir();\n await ensureDir(auditDir);\n\n let currentUser: string;\n try {\n currentUser = userInfo().username;\n } catch {\n currentUser = process.env.USER ?? process.env.USERNAME ?? 'unknown';\n }\n\n const partialEntry = {\n id: randomUUID().substring(0, 12),\n timestamp: new Date(),\n action,\n user: currentUser,\n projectPath,\n details,\n };\n\n const entry: AuditEntry = {\n ...partialEntry,\n integrityHash: computeIntegrityHash(partialEntry),\n };\n\n const auditFile = getCurrentAuditFile();\n let entries = await readJSON<AuditEntry[]>(auditFile) ?? [];\n entries.push(entry);\n\n if (entries.length > MAX_ENTRIES_PER_FILE) {\n entries = entries.slice(-MAX_ENTRIES_PER_FILE);\n }\n\n await writeJSON(auditFile, entries);\n try { await chmod(auditFile, 0o600); } catch { /* ignore on Windows */ }\n\n return entry;\n}\n\nexport async function getAuditEntries(\n options: {\n projectPath?: string;\n action?: AuditAction;\n since?: Date;\n limit?: number;\n } = {},\n): Promise<AuditEntry[]> {\n const auditDir = getAuditDir();\n let files: string[];\n try {\n files = await readdir(auditDir);\n } catch {\n return [];\n }\n\n const auditFiles = files\n .filter((f) => f.startsWith('audit_') && f.endsWith('.json'))\n .sort()\n .reverse();\n\n const allEntries: AuditEntry[] = [];\n const limit = options.limit ?? 100;\n\n for (const file of auditFiles) {\n if (allEntries.length >= limit) break;\n\n const entries = await readJSON<AuditEntry[]>(join(auditDir, file));\n if (!entries) continue;\n\n for (const entry of entries.reverse()) {\n if (allEntries.length >= limit) break;\n if (options.projectPath && entry.projectPath !== options.projectPath) continue;\n if (options.action && entry.action !== options.action) continue;\n if (options.since && new Date(entry.timestamp) < options.since) continue;\n allEntries.push(entry);\n }\n }\n\n return allEntries;\n}\n\nexport function verifyAuditEntry(entry: AuditEntry): boolean {\n const { integrityHash, ...rest } = entry;\n const expected = computeIntegrityHash(rest as Omit<AuditEntry, 'integrityHash'>);\n return expected === integrityHash;\n}\n\nexport async function verifyAuditIntegrity(): Promise<{\n totalEntries: number;\n validEntries: number;\n invalidEntries: AuditEntry[];\n}> {\n const entries = await getAuditEntries({ limit: 10000 });\n const invalidEntries: AuditEntry[] = [];\n\n for (const entry of entries) {\n if (!verifyAuditEntry(entry)) {\n invalidEntries.push(entry);\n }\n }\n\n return {\n totalEntries: entries.length,\n validEntries: entries.length - invalidEntries.length,\n invalidEntries,\n };\n}\n\nexport async function purgeOldAuditEntries(retentionDays: number): Promise<number> {\n const auditDir = getAuditDir();\n let files: string[];\n try {\n files = await readdir(auditDir);\n } catch {\n return 0;\n }\n\n const cutoff = new Date();\n cutoff.setDate(cutoff.getDate() - retentionDays);\n const cutoffStr = cutoff.toISOString().split('T')[0].replace(/-/g, '');\n\n let purged = 0;\n const { unlink } = await import('node:fs/promises');\n\n for (const file of files) {\n if (!file.startsWith('audit_') || !file.endsWith('.json')) continue;\n const dateStr = file.replace('audit_', '').replace('.json', '');\n if (dateStr < cutoffStr) {\n try {\n await unlink(join(auditDir, file));\n purged++;\n } catch { /* ignore */ }\n }\n }\n\n return purged;\n}\n","import { readFile, writeFile } from 'node:fs/promises';\nimport { readFileSync, existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { resolve, relative, join, dirname } from 'node:path';\nimport { createHash } from 'node:crypto';\nimport type { SecretFinding, SecretType } from '../types/govern.js';\n\n// ===== SECRET DETECTION ENGINE =====\n\ninterface SecretPattern {\n type: SecretType;\n pattern: RegExp;\n severity: SecretFinding['severity'];\n description: string;\n}\n\nconst BUILTIN_PATTERNS: { type: SecretType; source: string; flags: string; severity: SecretFinding['severity']; description: string }[] = [\n // API Keys\n { type: 'api-key', source: '(?:api[_-]?key|apikey)\\\\s*[:=]\\\\s*[\\'\"]?([a-zA-Z0-9_\\\\-]{20,})[\\'\"]?', flags: 'gi', severity: 'critical', description: 'API Key' },\n { type: 'api-key', source: 'sk-[a-zA-Z0-9]{20,}', flags: 'g', severity: 'critical', description: 'OpenAI/Anthropic API Key' },\n { type: 'api-key', source: 'sk-ant-[a-zA-Z0-9\\\\-]{20,}', flags: 'g', severity: 'critical', description: 'Anthropic API Key' },\n\n // AWS\n { type: 'aws-key', source: 'AKIA[0-9A-Z]{16}', flags: 'g', severity: 'critical', description: 'AWS Access Key ID' },\n { type: 'aws-key', source: '(?:aws_secret_access_key|aws_secret)\\\\s*[:=]\\\\s*[\\'\"]?([a-zA-Z0-9/+=]{40})[\\'\"]?', flags: 'gi', severity: 'critical', description: 'AWS Secret Key' },\n\n // Private Keys\n { type: 'private-key', source: '-----BEGIN (?:RSA |EC |DSA )?PRIVATE KEY-----', flags: 'g', severity: 'critical', description: 'Private Key' },\n { type: 'private-key', source: '-----BEGIN OPENSSH PRIVATE KEY-----', flags: 'g', severity: 'critical', description: 'SSH Private Key' },\n\n // Passwords\n { type: 'password', source: '(?:password|passwd|pwd)\\\\s*[:=]\\\\s*[\\'\"]([^\\'\"]{8,})[\\'\"](?!\\\\s*\\\\{)', flags: 'gi', severity: 'high', description: 'Hardcoded Password' },\n { type: 'password', source: '(?:DB_PASSWORD|DATABASE_PASSWORD|MYSQL_PASSWORD|POSTGRES_PASSWORD)\\\\s*[:=]\\\\s*[\\'\"]?([^\\'\"{}\\\\s]{4,})[\\'\"]?', flags: 'gi', severity: 'high', description: 'Database Password' },\n\n // Tokens\n { type: 'token', source: '(?:bearer|token|auth_token|access_token|refresh_token)\\\\s*[:=]\\\\s*[\\'\"]([a-zA-Z0-9_\\\\-.]{20,})[\\'\"](?!\\\\s*\\\\{)', flags: 'gi', severity: 'high', description: 'Auth Token' },\n { type: 'token', source: 'ghp_[a-zA-Z0-9]{36}', flags: 'g', severity: 'critical', description: 'GitHub Personal Access Token' },\n { type: 'token', source: 'gho_[a-zA-Z0-9]{36}', flags: 'g', severity: 'critical', description: 'GitHub OAuth Token' },\n { type: 'token', source: 'glpat-[a-zA-Z0-9\\\\-]{20,}', flags: 'g', severity: 'critical', description: 'GitLab Personal Access Token' },\n { type: 'token', source: 'npm_[a-zA-Z0-9]{36}', flags: 'g', severity: 'high', description: 'npm Token' },\n\n // Connection strings\n { type: 'connection-string', source: '(?:mongodb(?:\\\\+srv)?|postgres(?:ql)?|mysql|redis|amqp):\\\\/\\\\/[^\\\\s\\'\"]+:[^\\\\s\\'\"]+@[^\\\\s\\'\"]+', flags: 'gi', severity: 'critical', description: 'Database Connection String' },\n { type: 'connection-string', source: '(?:DATABASE_URL|REDIS_URL|MONGODB_URI)\\\\s*[:=]\\\\s*[\\'\"]?([^\\\\s\\'\"]{10,})[\\'\"]?', flags: 'gi', severity: 'high', description: 'Database URL' },\n\n // Environment variables with secrets\n { type: 'env-variable', source: '(?:SECRET|PRIVATE|ENCRYPTION)[_-]?(?:KEY|TOKEN|PASS)\\\\s*[:=]\\\\s*[\\'\"]?([^\\\\s\\'\"]{8,})[\\'\"]?', flags: 'gi', severity: 'high', description: 'Secret Environment Variable' },\n\n // Stripe\n { type: 'api-key', source: 'sk_live_[a-zA-Z0-9]{24,}', flags: 'g', severity: 'critical', description: 'Stripe Live Secret Key' },\n { type: 'api-key', source: 'pk_live_[a-zA-Z0-9]{24,}', flags: 'g', severity: 'high', description: 'Stripe Live Publishable Key' },\n { type: 'api-key', source: 'rk_live_[a-zA-Z0-9]{24,}', flags: 'g', severity: 'critical', description: 'Stripe Restricted Key' },\n\n // Slack\n { type: 'token', source: 'xoxb-[0-9]{10,}-[0-9]{10,}-[a-zA-Z0-9]{24,}', flags: 'g', severity: 'critical', description: 'Slack Bot Token' },\n { type: 'token', source: 'xoxp-[0-9]{10,}-[0-9]{10,}-[a-zA-Z0-9]{24,}', flags: 'g', severity: 'critical', description: 'Slack User Token' },\n { type: 'api-key', source: 'https://hooks\\\\.slack\\\\.com/services/T[a-zA-Z0-9_]+/B[a-zA-Z0-9_]+/[a-zA-Z0-9_]+', flags: 'g', severity: 'high', description: 'Slack Webhook URL' },\n\n // Google\n { type: 'api-key', source: 'AIza[0-9A-Za-z_-]{35}', flags: 'g', severity: 'high', description: 'Google API Key' },\n { type: 'token', source: 'ya29\\\\.[0-9A-Za-z_-]+', flags: 'g', severity: 'high', description: 'Google OAuth Token' },\n\n // Azure\n { type: 'api-key', source: '(?:AccountKey|SharedAccessKey)\\\\s*=\\\\s*[a-zA-Z0-9+/=]{40,}', flags: 'g', severity: 'critical', description: 'Azure Storage Key' },\n\n // Twilio\n { type: 'api-key', source: 'AC[a-f0-9]{32}', flags: 'g', severity: 'high', description: 'Twilio Account SID' },\n\n // SendGrid\n { type: 'api-key', source: 'SG\\\\.[a-zA-Z0-9_-]{22}\\\\.[a-zA-Z0-9_-]{43}', flags: 'g', severity: 'critical', description: 'SendGrid API Key' },\n\n // JWT\n { type: 'token', source: 'eyJ[a-zA-Z0-9_-]{10,}\\\\.eyJ[a-zA-Z0-9_-]{10,}\\\\.[a-zA-Z0-9_-]{10,}', flags: 'g', severity: 'high', description: 'JSON Web Token' },\n\n // Datadog\n { type: 'api-key', source: '(?:DD_API_KEY|DATADOG_API_KEY)\\\\s*[:=]\\\\s*[\\'\"]?([a-f0-9]{32})[\\'\"]?', flags: 'gi', severity: 'critical', description: 'Datadog API Key' },\n { type: 'api-key', source: '(?:DD_APP_KEY|DATADOG_APP_KEY)\\\\s*[:=]\\\\s*[\\'\"]?([a-f0-9]{40})[\\'\"]?', flags: 'gi', severity: 'critical', description: 'Datadog App Key' },\n\n // Sentry\n { type: 'connection-string', source: 'https://[a-f0-9]{32}@[a-z0-9]+\\\\.ingest\\\\.sentry\\\\.io/[0-9]+', flags: 'g', severity: 'high', description: 'Sentry DSN' },\n\n // Firebase\n { type: 'api-key', source: '(?:FIREBASE_API_KEY|FIREBASE_KEY)\\\\s*[:=]\\\\s*[\\'\"]?([a-zA-Z0-9_\\\\-]{30,})[\\'\"]?', flags: 'gi', severity: 'high', description: 'Firebase API Key' },\n { type: 'connection-string', source: 'firebase[a-z]*:\\\\/\\\\/[^\\\\s\\'\"]+', flags: 'gi', severity: 'high', description: 'Firebase URL' },\n\n // Supabase\n { type: 'api-key', source: 'sbp_[a-f0-9]{40}', flags: 'g', severity: 'critical', description: 'Supabase Service Key' },\n { type: 'token', source: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\\\\.[a-zA-Z0-9_-]{20,}\\\\.[a-zA-Z0-9_-]{20,}', flags: 'g', severity: 'high', description: 'Supabase Anon/Service JWT' },\n\n // Vercel\n { type: 'token', source: '(?:VERCEL_TOKEN|VERCEL_API_TOKEN)\\\\s*[:=]\\\\s*[\\'\"]?([a-zA-Z0-9]{24,})[\\'\"]?', flags: 'gi', severity: 'critical', description: 'Vercel Token' },\n\n // Heroku\n { type: 'api-key', source: '(?:HEROKU_API_KEY|HEROKU_TOKEN)\\\\s*[:=]\\\\s*[\\'\"]?([a-f0-9\\\\-]{36,})[\\'\"]?', flags: 'gi', severity: 'critical', description: 'Heroku API Key' },\n\n // DigitalOcean\n { type: 'token', source: 'dop_v1_[a-f0-9]{64}', flags: 'g', severity: 'critical', description: 'DigitalOcean Personal Access Token' },\n { type: 'token', source: 'doo_v1_[a-f0-9]{64}', flags: 'g', severity: 'critical', description: 'DigitalOcean OAuth Token' },\n\n // Mailgun\n { type: 'api-key', source: 'key-[a-zA-Z0-9]{32}', flags: 'g', severity: 'high', description: 'Mailgun API Key' },\n\n // PII\n { type: 'pii', source: '\\\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\\\.[A-Z|a-z]{2,}\\\\b', flags: 'g', severity: 'medium', description: 'Email Address (PII)' },\n { type: 'pii', source: '\\\\b(?!000|666|9\\\\d{2})(\\\\d{3})[-.]?(?!00)(\\\\d{2})[-.]?(?!0000)(\\\\d{4})\\\\b', flags: 'g', severity: 'high', description: 'Possible SSN (PII)' },\n];\n\n// Pre-compiled builtin patterns (compiled once, reused across all calls)\nlet _cachedBuiltinPatterns: SecretPattern[] | null = null;\n\nfunction getBuiltinPatterns(): SecretPattern[] {\n if (!_cachedBuiltinPatterns) {\n _cachedBuiltinPatterns = BUILTIN_PATTERNS.map((def) => ({\n type: def.type,\n pattern: new RegExp(def.source, def.flags),\n severity: def.severity,\n description: def.description,\n }));\n }\n return _cachedBuiltinPatterns;\n}\n\nfunction buildPatterns(customPatterns: string[] = []): SecretPattern[] {\n const builtins = getBuiltinPatterns();\n if (customPatterns.length === 0) return builtins;\n\n // Only create new array when custom patterns exist\n const patterns = [...builtins];\n for (const custom of customPatterns) {\n try {\n patterns.push({\n type: 'custom',\n pattern: new RegExp(custom, 'gi'),\n severity: 'medium',\n description: `Custom pattern: ${custom}`,\n });\n } catch { /* skip invalid regex */ }\n }\n\n return patterns;\n}\n\nexport function scanContentForSecrets(\n content: string,\n filePath: string,\n customPatterns: string[] = [],\n extraPiiSafeDomains?: Set<string>,\n): SecretFinding[] {\n const findings: SecretFinding[] = [];\n const lines = content.split('\\n');\n const allPatterns = buildPatterns(customPatterns);\n\n for (const secretPattern of allPatterns) {\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n secretPattern.pattern.lastIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = secretPattern.pattern.exec(line)) !== null) {\n const matchText = match[0];\n if (isTemplateOrPlaceholder(matchText)) continue;\n if (secretPattern.type === 'pii' && isSafeEmail(matchText, extraPiiSafeDomains)) continue;\n\n findings.push({\n type: secretPattern.type,\n file: filePath,\n line: i + 1,\n match: matchText,\n redacted: redactSecret(matchText),\n severity: secretPattern.severity,\n });\n }\n }\n }\n\n return deduplicateFindings(findings);\n}\n\nexport async function scanFileForSecrets(\n filePath: string,\n projectPath: string,\n customPatterns: string[] = [],\n): Promise<SecretFinding[]> {\n try {\n const content = await readFile(filePath, 'utf-8');\n const relPath = relative(resolve(projectPath), resolve(filePath));\n return scanContentForSecrets(content, relPath, customPatterns);\n } catch {\n return [];\n }\n}\n\nexport async function scanProjectForSecrets(\n projectPath: string,\n filePaths: string[],\n customPatterns: string[] = [],\n): Promise<SecretFinding[]> {\n const allFindings: SecretFinding[] = [];\n\n for (const fp of filePaths) {\n const findings = await scanFileForSecrets(fp, projectPath, customPatterns);\n allFindings.push(...findings);\n }\n\n return allFindings.sort((a, b) => {\n const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };\n return severityOrder[a.severity] - severityOrder[b.severity];\n });\n}\n\nexport function sanitizeContent(content: string, customPatterns: string[] = []): string {\n let sanitized = content;\n const allPatterns = buildPatterns(customPatterns);\n\n for (const secretPattern of allPatterns) {\n sanitized = sanitized.replace(secretPattern.pattern, (match) => {\n if (isTemplateOrPlaceholder(match)) return match;\n return redactSecret(match);\n });\n }\n\n return sanitized;\n}\n\nfunction redactSecret(value: string): string {\n if (value.length <= 8) return '***REDACTED***';\n const prefix = value.substring(0, 4);\n const suffix = value.substring(value.length - 2);\n return `${prefix}${'*'.repeat(Math.min(value.length - 6, 20))}${suffix}`;\n}\n\nfunction isTemplateOrPlaceholder(value: string): boolean {\n const placeholders = [\n /\\$\\{.*\\}/, /\\{\\{.*\\}\\}/, /%[sd]/, /<[A-Z_]+>/, /YOUR_.*_HERE/i,\n /\\bCHANGE_ME\\b/i, /\\bPLACEHOLDER\\b/i, /\\bexample\\b/i, /\\bTODO\\b/i, /xxx+/i,\n /\\breplace.?me\\b/i, /\\bdummy\\b/i, /\\btest_?key\\b/i, /\\bsample\\b/i,\n ];\n return placeholders.some((p) => p.test(value));\n}\n\n// ===== PII SAFE DOMAIN LIST =====\n// Emails at these domains are not flagged as PII (too many false positives)\n\nconst PII_SAFE_EMAIL_DOMAINS = new Set([\n 'example.com', 'example.org', 'example.net',\n 'test.com', 'test.org', 'test.net',\n 'localhost', 'localhost.localdomain',\n 'email.com', 'mail.com',\n 'foo.com', 'bar.com', 'baz.com',\n 'acme.com', 'company.com', 'corp.com',\n 'noreply.com', 'no-reply.com',\n 'users.noreply.github.com',\n 'placeholder.com',\n]);\n\nfunction isSafeEmail(value: string, extraDomains?: Set<string>): boolean {\n const match = value.match(/@([a-zA-Z0-9.-]+)$/);\n if (!match) return false;\n const domain = match[1].toLowerCase();\n if (PII_SAFE_EMAIL_DOMAINS.has(domain)) return true;\n if (extraDomains && extraDomains.has(domain)) return true;\n return false;\n}\n\nfunction deduplicateFindings(findings: SecretFinding[]): SecretFinding[] {\n const seen = new Set<string>();\n return findings.filter((f) => {\n const key = `${f.file}:${f.line}:${f.type}:${f.match}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n\n// ===== ALLOWLIST — Mark findings as reviewed/safe =====\n\nexport interface AllowlistEntry {\n fingerprint: string; // sha256 of file:type:match\n file: string;\n type: string;\n redacted: string;\n reason: string;\n reviewedBy: string;\n reviewedAt: string;\n}\n\nfunction fingerprintFinding(f: SecretFinding): string {\n return createHash('sha256').update(`${f.file}:${f.type}:${f.match}`).digest('hex').slice(0, 32);\n}\n\nfunction getAllowlistPath(projectPath: string): string {\n return join(projectPath, '.cto', 'audit', 'allowlist.json');\n}\n\nexport function loadAllowlist(projectPath: string): AllowlistEntry[] {\n const filePath = getAllowlistPath(projectPath);\n if (!existsSync(filePath)) return [];\n try {\n return JSON.parse(readFileSync(filePath, 'utf-8'));\n } catch {\n return [];\n }\n}\n\nexport function saveAllowlist(projectPath: string, entries: AllowlistEntry[]): void {\n const filePath = getAllowlistPath(projectPath);\n mkdirSync(dirname(filePath), { recursive: true });\n writeFileSync(filePath, JSON.stringify(entries, null, 2) + '\\n');\n}\n\nexport function addToAllowlist(\n projectPath: string,\n finding: SecretFinding,\n reason: string,\n reviewedBy: string = 'manual',\n): AllowlistEntry {\n const entries = loadAllowlist(projectPath);\n const entry: AllowlistEntry = {\n fingerprint: fingerprintFinding(finding),\n file: finding.file,\n type: finding.type,\n redacted: finding.redacted,\n reason,\n reviewedBy,\n reviewedAt: new Date().toISOString(),\n };\n\n // Deduplicate by fingerprint\n const existing = entries.findIndex((e) => e.fingerprint === entry.fingerprint);\n if (existing >= 0) {\n entries[existing] = entry;\n } else {\n entries.push(entry);\n }\n\n saveAllowlist(projectPath, entries);\n return entry;\n}\n\nexport function filterByAllowlist(findings: SecretFinding[], projectPath: string): {\n filtered: SecretFinding[];\n allowed: SecretFinding[];\n} {\n const allowlist = loadAllowlist(projectPath);\n if (allowlist.length === 0) return { filtered: findings, allowed: [] };\n\n const allowedFingerprints = new Set(allowlist.map((e) => e.fingerprint));\n const filtered: SecretFinding[] = [];\n const allowed: SecretFinding[] = [];\n\n for (const f of findings) {\n if (allowedFingerprints.has(fingerprintFinding(f))) {\n allowed.push(f);\n } else {\n filtered.push(f);\n }\n }\n\n return { filtered, allowed };\n}\n\n// ===== INCREMENTAL SCANNING — Only scan changed files =====\n\ninterface FileHashMap {\n [relativePath: string]: string; // sha256 of file content\n}\n\nfunction getHashCachePath(projectPath: string): string {\n return join(projectPath, '.cto', 'audit', '.hashcache.json');\n}\n\nfunction loadHashCache(projectPath: string): FileHashMap {\n const filePath = getHashCachePath(projectPath);\n if (!existsSync(filePath)) return {};\n try {\n return JSON.parse(readFileSync(filePath, 'utf-8'));\n } catch {\n return {};\n }\n}\n\nfunction saveHashCache(projectPath: string, cache: FileHashMap): void {\n const filePath = getHashCachePath(projectPath);\n mkdirSync(dirname(filePath), { recursive: true });\n writeFileSync(filePath, JSON.stringify(cache));\n}\n\nfunction hashContent(content: string): string {\n return createHash('sha256').update(content).digest('hex').slice(0, 16);\n}\n\nexport function getChangedFiles(\n projectPath: string,\n filePaths: string[],\n): { changed: string[]; unchanged: string[]; cache: FileHashMap } {\n const oldCache = loadHashCache(projectPath);\n const newCache: FileHashMap = {};\n const changed: string[] = [];\n const unchanged: string[] = [];\n\n for (const fp of filePaths) {\n try {\n const content = readFileSync(fp, 'utf-8');\n const relPath = relative(resolve(projectPath), resolve(fp));\n const hash = hashContent(content);\n newCache[relPath] = hash;\n\n if (oldCache[relPath] === hash) {\n unchanged.push(fp);\n } else {\n changed.push(fp);\n }\n } catch {\n changed.push(fp); // Can't read → scan it\n }\n }\n\n return { changed, unchanged, cache: newCache };\n}\n\n// ===== SEVERITY CONFIGURATION =====\n\nexport interface AuditConfig {\n severityOverrides: Partial<Record<SecretType, SecretFinding['severity']>>;\n piiSafeDomains: string[];\n customPatterns: string[];\n entropyThreshold: number;\n includePII: boolean;\n incrementalScan: boolean;\n}\n\nexport const DEFAULT_AUDIT_CONFIG: AuditConfig = {\n severityOverrides: {},\n piiSafeDomains: [],\n customPatterns: [],\n entropyThreshold: 5.0,\n includePII: true,\n incrementalScan: true,\n};\n\nfunction getAuditConfigPath(projectPath: string): string {\n return join(projectPath, '.cto', 'audit', 'config.json');\n}\n\nexport function loadAuditConfig(projectPath: string): AuditConfig {\n const filePath = getAuditConfigPath(projectPath);\n if (!existsSync(filePath)) return { ...DEFAULT_AUDIT_CONFIG };\n try {\n const loaded = JSON.parse(readFileSync(filePath, 'utf-8'));\n return { ...DEFAULT_AUDIT_CONFIG, ...loaded };\n } catch {\n return { ...DEFAULT_AUDIT_CONFIG };\n }\n}\n\nexport function saveAuditConfig(projectPath: string, config: AuditConfig): void {\n const filePath = getAuditConfigPath(projectPath);\n mkdirSync(dirname(filePath), { recursive: true });\n writeFileSync(filePath, JSON.stringify(config, null, 2) + '\\n');\n}\n\nfunction applySeverityOverrides(\n findings: SecretFinding[],\n overrides: Partial<Record<SecretType, SecretFinding['severity']>>,\n): SecretFinding[] {\n if (Object.keys(overrides).length === 0) return findings;\n return findings.map((f) => {\n const override = overrides[f.type];\n if (override) return { ...f, severity: override };\n return f;\n });\n}\n\n// ===== PRE-COMMIT HOOK GENERATOR =====\n\nexport function generatePreCommitHook(projectPath: string, hookType: 'husky' | 'githooks' = 'husky'): string {\n const hookContent = `#!/bin/sh\n# CTO Secret Detection — Pre-commit hook\n# Auto-generated by: npx cto-ai-cli --audit --init-hook\n# Scans ONLY staged files for secrets before allowing commit.\n\nSTAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)\n\nif [ -z \"$STAGED_FILES\" ]; then\n exit 0\nfi\n\necho \"🔍 CTO: Scanning $(echo \"$STAGED_FILES\" | wc -l | tr -d ' ') staged files for secrets...\"\n\n# Write staged files to temp list\nTMPFILE=$(mktemp)\necho \"$STAGED_FILES\" > \"$TMPFILE\"\n\n# Run audit in CI mode on staged files only\nCI=true npx cto-ai-cli --audit --files \"$TMPFILE\"\nRESULT=$?\n\nrm -f \"$TMPFILE\"\n\nif [ $RESULT -ne 0 ]; then\n echo \"\"\n echo \"❌ Commit blocked: secrets detected in staged files.\"\n echo \" Run 'npx cto-ai-cli --audit' to see details.\"\n echo \" Use allowlist to mark reviewed findings as safe.\"\n echo \"\"\n exit 1\nfi\n\necho \"✅ No secrets detected. Proceeding with commit.\"\n`;\n\n let hookPath: string;\n if (hookType === 'husky') {\n hookPath = join(projectPath, '.husky', 'pre-commit');\n } else {\n hookPath = join(projectPath, '.git', 'hooks', 'pre-commit');\n }\n\n mkdirSync(dirname(hookPath), { recursive: true });\n writeFileSync(hookPath, hookContent, { mode: 0o755 });\n\n return hookPath;\n}\n\n// ===== ENTROPY ANALYSIS =====\n\nfunction shannonEntropy(str: string): number {\n const freq = new Map<string, number>();\n for (const ch of str) {\n freq.set(ch, (freq.get(ch) || 0) + 1);\n }\n let entropy = 0;\n for (const count of freq.values()) {\n const p = count / str.length;\n if (p > 0) entropy -= p * Math.log2(p);\n }\n return entropy;\n}\n\nconst HIGH_ENTROPY_RE = /['\"]([a-zA-Z0-9+/=_\\-]{30,})['\"]|=\\s*['\"]?([a-zA-Z0-9+/=_\\-]{30,})['\"]?/g;\n\nconst ENTROPY_SKIP = [\n /^[a-f0-9]{32,}$/i, // hex hashes\n /^[A-Z_]{30,}$/, // all-caps constants\n /^[a-z_]{30,}$/, // all-lowercase identifiers\n /^[a-zA-Z0-9+/]+=+$/, // base64 padding\n /^[a-z]+[A-Z][a-zA-Z]+$/, // camelCase identifiers\n /sha\\d+-/i, // integrity hashes (sha256-, sha512-)\n];\n\nexport function scanContentForHighEntropy(\n content: string,\n filePath: string,\n threshold: number = 5.0,\n): SecretFinding[] {\n const findings: SecretFinding[] = [];\n const lines = content.split('\\n');\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (line.trim().startsWith('//') || line.trim().startsWith('#') || line.trim().startsWith('*')) continue;\n\n HIGH_ENTROPY_RE.lastIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = HIGH_ENTROPY_RE.exec(line)) !== null) {\n const value = match[1] || match[2];\n if (!value || value.length < 40) continue;\n if (isTemplateOrPlaceholder(value)) continue;\n if (ENTROPY_SKIP.some((p) => p.test(value))) continue;\n\n const entropy = shannonEntropy(value);\n if (entropy >= threshold) {\n findings.push({\n type: 'high-entropy',\n file: filePath,\n line: i + 1,\n match: value,\n redacted: redactSecret(value),\n severity: entropy >= 5.0 ? 'high' : 'medium',\n });\n }\n }\n }\n\n return deduplicateFindings(findings);\n}\n\n// ===== FULL PROJECT AUDIT =====\n\nexport interface AuditResult {\n findings: SecretFinding[];\n summary: {\n totalFiles: number;\n filesScanned: number;\n filesWithSecrets: number;\n totalFindings: number;\n bySeverity: { critical: number; high: number; medium: number; low: number };\n byType: Record<string, number>;\n };\n recommendations: string[];\n}\n\nexport interface AuditOptions {\n customPatterns?: string[];\n entropyThreshold?: number;\n includePII?: boolean;\n useAllowlist?: boolean;\n incrementalScan?: boolean;\n severityOverrides?: Partial<Record<SecretType, SecretFinding['severity']>>;\n piiSafeDomains?: string[];\n}\n\nexport async function auditProject(\n projectPath: string,\n filePaths: string[],\n options: AuditOptions = {},\n): Promise<AuditResult> {\n // Load config from .cto/audit/config.json (user overrides take precedence)\n const savedConfig = loadAuditConfig(projectPath);\n const customPatterns = options.customPatterns ?? savedConfig.customPatterns;\n const entropyThreshold = options.entropyThreshold ?? savedConfig.entropyThreshold;\n const includePII = options.includePII ?? savedConfig.includePII;\n const useAllowlist = options.useAllowlist ?? true;\n const incrementalScan = options.incrementalScan ?? savedConfig.incrementalScan;\n const severityOverrides = options.severityOverrides ?? savedConfig.severityOverrides;\n\n // Build per-call PII safe domains set (no global mutation)\n let extraPiiDomains: Set<string> | undefined;\n const allExtraDomains = [...(options.piiSafeDomains || []), ...savedConfig.piiSafeDomains];\n if (allExtraDomains.length > 0) {\n extraPiiDomains = new Set(allExtraDomains.map((d) => d.toLowerCase()));\n }\n\n // Incremental scanning: only scan changed files\n let filesToScan = filePaths;\n let unchangedCount = 0;\n let newCache: Record<string, string> | null = null;\n\n if (incrementalScan) {\n const { changed, unchanged, cache } = getChangedFiles(projectPath, filePaths);\n newCache = cache; // Always save cache for next run\n if (changed.length < filePaths.length) {\n filesToScan = changed;\n unchangedCount = unchanged.length;\n }\n }\n\n const allFindings: SecretFinding[] = [];\n const filesWithSecrets = new Set<string>();\n\n for (const fp of filesToScan) {\n try {\n const content = await readFile(fp, 'utf-8');\n const relPath = relative(resolve(projectPath), resolve(fp));\n\n // Skip test files and declaration files for entropy (too many false positives)\n const isTestFile = /\\.(test|spec|mock)\\.[jt]sx?$/.test(relPath) || relPath.includes('__tests__');\n const isDtsFile = relPath.endsWith('.d.ts');\n\n // Pattern-based detection (pass extra PII domains per-call, no global leak)\n let findings = scanContentForSecrets(content, relPath, customPatterns, extraPiiDomains);\n\n // Filter PII if not wanted\n if (!includePII) {\n findings = findings.filter((f) => f.type !== 'pii');\n }\n\n // Entropy-based detection (skip test/declaration files — too noisy)\n const entropyFindings = (isTestFile || isDtsFile)\n ? []\n : scanContentForHighEntropy(content, relPath, entropyThreshold);\n\n const combined = [...findings, ...entropyFindings];\n if (combined.length > 0) {\n filesWithSecrets.add(relPath);\n allFindings.push(...combined);\n }\n } catch {\n // skip unreadable files\n }\n }\n\n // Apply severity overrides from config\n let finalFindings = applySeverityOverrides(allFindings, severityOverrides);\n\n // Apply allowlist — filter out reviewed findings\n let allowedCount = 0;\n if (useAllowlist) {\n const { filtered, allowed } = filterByAllowlist(finalFindings, projectPath);\n finalFindings = filtered;\n allowedCount = allowed.length;\n }\n\n // Sort by severity\n finalFindings.sort((a, b) => {\n const order = { critical: 0, high: 1, medium: 2, low: 3 };\n return order[a.severity] - order[b.severity];\n });\n\n // Save hash cache for incremental scanning\n if (newCache) {\n saveHashCache(projectPath, newCache);\n }\n\n // Build summary\n const bySeverity = { critical: 0, high: 0, medium: 0, low: 0 };\n const byType: Record<string, number> = {};\n for (const f of finalFindings) {\n bySeverity[f.severity]++;\n byType[f.type] = (byType[f.type] || 0) + 1;\n }\n\n // Generate recommendations\n const recommendations: string[] = [];\n if (bySeverity.critical > 0) {\n recommendations.push('CRITICAL: Rotate all detected credentials immediately. They may already be compromised.');\n }\n if (byType['password'] > 0) {\n recommendations.push('Move passwords to environment variables or a secrets manager (AWS Secrets Manager, Vault, etc.).');\n }\n if (byType['api-key'] > 0 || byType['aws-key'] > 0) {\n recommendations.push('Use environment variables for API keys. Never commit them to source control.');\n }\n if (byType['connection-string'] > 0) {\n recommendations.push('Database connection strings should use environment variables, not hardcoded values.');\n }\n if (byType['private-key'] > 0) {\n recommendations.push('Private keys should NEVER be in source code. Use a key management service.');\n }\n if (byType['pii'] > 0) {\n recommendations.push('PII detected. Review for GDPR/CCPA compliance. Consider data anonymization.');\n }\n if (byType['high-entropy'] > 0) {\n recommendations.push('High-entropy strings detected that may be secrets. Review manually.');\n }\n if (finalFindings.length > 0) {\n recommendations.push('Add a .gitignore entry for .env files if not already present.');\n recommendations.push('Run `npx cto-ai-cli --audit` regularly or add to CI pipeline.');\n }\n if (finalFindings.length === 0) {\n recommendations.push('No secrets detected. Great job keeping your codebase clean!');\n }\n if (allowedCount > 0) {\n recommendations.push(`${allowedCount} finding(s) skipped via allowlist (.cto/audit/allowlist.json).`);\n }\n if (unchangedCount > 0) {\n recommendations.push(`${unchangedCount} unchanged file(s) skipped (incremental scan).`);\n }\n\n return {\n findings: finalFindings,\n summary: {\n totalFiles: filePaths.length,\n filesScanned: filesToScan.length,\n filesWithSecrets: filesWithSecrets.size,\n totalFindings: finalFindings.length,\n bySeverity,\n byType,\n },\n recommendations,\n };\n}\n","import type { GraphEdge } from '../types/engine.js';\n\n// ===== SHARED GRAPH UTILITIES =====\n\nexport interface AdjacencyList {\n forward: Map<string, string[]>; // file → files it imports\n reverse: Map<string, string[]>; // file → files that import it\n}\n\nexport function buildAdjacencyList(edges: GraphEdge[]): AdjacencyList {\n const forward = new Map<string, string[]>();\n const reverse = new Map<string, string[]>();\n\n for (const edge of edges) {\n if (!forward.has(edge.from)) forward.set(edge.from, []);\n forward.get(edge.from)!.push(edge.to);\n\n if (!reverse.has(edge.to)) reverse.set(edge.to, []);\n reverse.get(edge.to)!.push(edge.from);\n }\n\n return { forward, reverse };\n}\n\nexport function bfsBidirectional(\n seeds: string[],\n adj: AdjacencyList,\n depth: number,\n): Set<string> {\n const result = new Set(seeds);\n let frontier = [...seeds];\n const visited = new Set<string>();\n\n for (let d = 0; d < depth; d++) {\n const nextFrontier: string[] = [];\n\n for (const node of frontier) {\n if (visited.has(node)) continue;\n visited.add(node);\n\n // Forward neighbors (imports)\n const fwd = adj.forward.get(node);\n if (fwd) {\n for (const neighbor of fwd) {\n if (!visited.has(neighbor)) {\n result.add(neighbor);\n nextFrontier.push(neighbor);\n }\n }\n }\n\n // Reverse neighbors (imported by)\n const rev = adj.reverse.get(node);\n if (rev) {\n for (const neighbor of rev) {\n if (!visited.has(neighbor)) {\n result.add(neighbor);\n nextFrontier.push(neighbor);\n }\n }\n }\n }\n\n frontier = nextFrontier;\n }\n\n return result;\n}\n\nexport function matchGlob(path: string, pattern: string): boolean {\n const regexStr = pattern\n .replace(/\\./g, '\\\\.')\n .replace(/\\*\\*/g, '§§')\n .replace(/\\*/g, '[^/]*')\n .replace(/§§/g, '.*')\n .replace(/\\?/g, '.');\n\n try {\n return new RegExp(`^${regexStr}$`).test(path);\n } catch {\n return false;\n }\n}\n","import type {\n PolicySet,\n PolicyRule,\n PolicyRuleType,\n PolicyValidation,\n PolicyViolation,\n PolicyWarning,\n} from '../types/govern.js';\nimport type { ContextSelection, AnalyzedFile } from '../types/engine.js';\nimport { matchGlob } from '../engine/graph-utils.js';\n\n// ===== POLICY ENGINE =====\n//\n// YAML-backed rule engine for controlling what context gets included/excluded.\n// Policies are evaluated after selection to validate compliance.\n\nexport const DEFAULT_POLICY: PolicySet = {\n version: '1.0',\n name: 'default',\n rules: [\n {\n id: 'no-env',\n type: 'exclude-always',\n pattern: '**/*.env*',\n reason: 'Environment files must never be sent to AI',\n enabled: true,\n },\n {\n id: 'no-secrets',\n type: 'secret-block',\n reason: 'Files with detected secrets are blocked',\n enabled: true,\n },\n {\n id: 'min-coverage',\n type: 'coverage-minimum',\n threshold: 70,\n reason: 'Warn if context coverage drops below 70%',\n enabled: true,\n },\n ],\n};\n\n// ===== VALIDATION =====\n\nexport function validateSelection(\n selection: ContextSelection,\n policies: PolicySet,\n allFiles?: AnalyzedFile[],\n): PolicyValidation {\n const violations: PolicyViolation[] = [];\n const warnings: PolicyWarning[] = [];\n\n const includedPaths = new Set(selection.files.map((f) => f.relativePath));\n\n for (const rule of policies.rules) {\n if (!rule.enabled) continue;\n\n switch (rule.type) {\n case 'exclude-always': {\n if (!rule.pattern) break;\n const violatingFiles = selection.files.filter((f) =>\n matchGlob(f.relativePath, rule.pattern!),\n );\n for (const f of violatingFiles) {\n violations.push({\n rule,\n message: `File \"${f.relativePath}\" is included but matches exclude-always pattern \"${rule.pattern}\"`,\n severity: 'error',\n });\n }\n break;\n }\n\n case 'include-always': {\n if (!rule.pattern || !allFiles) break;\n const requiredFiles = allFiles.filter((f) =>\n matchGlob(f.relativePath, rule.pattern!),\n );\n for (const f of requiredFiles) {\n if (!includedPaths.has(f.relativePath)) {\n violations.push({\n rule,\n message: `File \"${f.relativePath}\" matches include-always pattern \"${rule.pattern}\" but is not included`,\n severity: 'warning',\n });\n }\n }\n break;\n }\n\n case 'coverage-minimum': {\n const threshold = rule.threshold ?? 70;\n if (selection.coverage.score < threshold) {\n warnings.push({\n rule,\n message: `Coverage ${selection.coverage.score}% is below minimum ${threshold}%`,\n currentValue: selection.coverage.score,\n threshold,\n });\n }\n break;\n }\n\n case 'risk-maximum': {\n const threshold = rule.threshold ?? 50;\n if (selection.riskScore > threshold) {\n warnings.push({\n rule,\n message: `Exclusion risk ${selection.riskScore}/100 exceeds maximum ${threshold}`,\n currentValue: selection.riskScore,\n threshold,\n });\n }\n break;\n }\n\n case 'budget-limit': {\n if (!rule.category || !rule.threshold) break;\n const categoryFiles = selection.files.filter((f) =>\n fileMatchesCategory(f.relativePath, rule.category!),\n );\n const categoryTokens = categoryFiles.reduce((s, f) => s + f.tokens, 0);\n const categoryPercent = selection.totalTokens > 0\n ? (categoryTokens / selection.totalTokens) * 100\n : 0;\n\n if (categoryPercent > rule.threshold) {\n warnings.push({\n rule,\n message: `Category \"${rule.category}\" uses ${Math.round(categoryPercent)}% of budget (max: ${rule.threshold}%)`,\n currentValue: Math.round(categoryPercent),\n threshold: rule.threshold,\n });\n }\n break;\n }\n }\n }\n\n return {\n passed: violations.filter((v) => v.severity === 'error').length === 0,\n violations,\n warnings,\n };\n}\n\n// ===== POLICY CRUD =====\n\nexport function addRule(policies: PolicySet, rule: PolicyRule): PolicySet {\n return {\n ...policies,\n rules: [...policies.rules, rule],\n };\n}\n\nexport function removeRule(policies: PolicySet, ruleId: string): PolicySet {\n return {\n ...policies,\n rules: policies.rules.filter((r) => r.id !== ruleId),\n };\n}\n\nexport function toggleRule(policies: PolicySet, ruleId: string, enabled: boolean): PolicySet {\n return {\n ...policies,\n rules: policies.rules.map((r) =>\n r.id === ruleId ? { ...r, enabled } : r,\n ),\n };\n}\n\n// ===== HELPERS =====\n\nfunction fileMatchesCategory(path: string, category: string): boolean {\n switch (category) {\n case 'test':\n return /\\.(test|spec)\\.[jt]sx?$/.test(path) || /\\/__tests__\\//.test(path) || /\\/tests?\\//.test(path);\n case 'config':\n return /\\.(config|rc)\\.[jt]s$/.test(path) || /\\.json$/.test(path) || /\\.ya?ml$/.test(path);\n case 'docs':\n return /\\.(md|txt|rst)$/.test(path);\n case 'types':\n return /types?\\//i.test(path) || /\\.d\\.ts$/.test(path);\n default:\n return path.includes(category);\n }\n}\n","import { randomUUID, createHash } from 'node:crypto';\nimport { readFile } from 'node:fs/promises';\nimport type {\n ContextSnapshot,\n SnapshotFile,\n SnapshotVerification,\n} from '../types/govern.js';\nimport type { ContextSelection, ProjectAnalysis } from '../types/engine.js';\n\n// ===== CONTEXT SNAPSHOTS =====\n//\n// Reproducible, verifiable snapshots of context selections.\n// Used for: deterministic mode, audit evidence, before/after comparisons.\n\nexport function createSnapshot(\n name: string,\n analysis: ProjectAnalysis,\n selection: ContextSelection,\n metadata: Record<string, unknown> = {},\n): ContextSnapshot {\n const files: SnapshotFile[] = selection.files.map((f) => ({\n relativePath: f.relativePath,\n hash: hashString(`${f.relativePath}:${f.tokens}:${f.pruneLevel}`),\n tokens: f.tokens,\n pruneLevel: f.pruneLevel,\n }));\n\n const snapshotData = files\n .map((f) => `${f.relativePath}:${f.hash}:${f.pruneLevel}`)\n .sort()\n .join('|');\n\n return {\n id: randomUUID().substring(0, 8),\n name,\n createdAt: new Date(),\n hash: hashString(snapshotData),\n projectHash: analysis.hash,\n analysisHash: analysis.hash,\n selectionHash: selection.hash,\n files,\n totalTokens: selection.totalTokens,\n coverageScore: selection.coverage.score,\n riskScore: selection.riskScore,\n metadata,\n };\n}\n\nexport async function verifySnapshot(\n snapshot: ContextSnapshot,\n currentAnalysis: ProjectAnalysis,\n currentSelection: ContextSelection,\n): Promise<SnapshotVerification> {\n const currentFiles = new Map(\n currentSelection.files.map((f) => [f.relativePath, f]),\n );\n\n let filesMatched = 0;\n const filesMissing: string[] = [];\n const filesChanged: string[] = [];\n\n for (const snapFile of snapshot.files) {\n const current = currentFiles.get(snapFile.relativePath);\n\n if (!current) {\n filesMissing.push(snapFile.relativePath);\n continue;\n }\n\n const currentHash = hashString(\n `${current.relativePath}:${current.tokens}:${current.pruneLevel}`,\n );\n\n if (currentHash === snapFile.hash) {\n filesMatched++;\n } else {\n filesChanged.push(snapFile.relativePath);\n }\n }\n\n // Verify overall hash\n const currentSnapshotData = snapshot.files\n .map((f) => {\n const current = currentFiles.get(f.relativePath);\n if (!current) return `${f.relativePath}:MISSING:MISSING`;\n return `${current.relativePath}:${hashString(`${current.relativePath}:${current.tokens}:${current.pruneLevel}`)}:${current.pruneLevel}`;\n })\n .sort()\n .join('|');\n\n const currentHash = hashString(currentSnapshotData);\n const integrityOk = currentHash === snapshot.hash && filesMissing.length === 0 && filesChanged.length === 0;\n\n return {\n valid: integrityOk,\n snapshotId: snapshot.id,\n filesChecked: snapshot.files.length,\n filesMatched,\n filesMissing,\n filesChanged,\n integrityOk,\n };\n}\n\nexport function compareSnapshots(\n older: ContextSnapshot,\n newer: ContextSnapshot,\n): {\n added: string[];\n removed: string[];\n changed: string[];\n tokenDelta: number;\n coverageDelta: number;\n riskDelta: number;\n} {\n const olderFiles = new Map(older.files.map((f) => [f.relativePath, f]));\n const newerFiles = new Map(newer.files.map((f) => [f.relativePath, f]));\n\n const added: string[] = [];\n const removed: string[] = [];\n const changed: string[] = [];\n\n for (const [path, file] of newerFiles) {\n const old = olderFiles.get(path);\n if (!old) {\n added.push(path);\n } else if (old.hash !== file.hash) {\n changed.push(path);\n }\n }\n\n for (const path of olderFiles.keys()) {\n if (!newerFiles.has(path)) {\n removed.push(path);\n }\n }\n\n return {\n added,\n removed,\n changed,\n tokenDelta: newer.totalTokens - older.totalTokens,\n coverageDelta: newer.coverageScore - older.coverageScore,\n riskDelta: newer.riskScore - older.riskScore,\n };\n}\n\n// ===== HELPERS =====\n\nfunction hashString(input: string): string {\n return createHash('sha256').update(input).digest('hex').substring(0, 16);\n}\n","import { createHash } from 'node:crypto';\nimport { readFile, chmod, readdir, stat } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { IntegrityManifest, IntegrityEntry } from '../types/govern.js';\n\n// ===== INTEGRITY VERIFICATION =====\n//\n// SHA-256 hash-based integrity checking for CTO artifacts.\n// Detects tampering of snapshots, audit logs, configs, and policies.\n\nexport function hashContent(content: Buffer | string): string {\n return createHash('sha256').update(content).digest('hex');\n}\n\nexport async function hashFile(filePath: string): Promise<string | null> {\n try {\n const content = await readFile(filePath);\n return hashContent(content);\n } catch {\n return null;\n }\n}\n\nexport async function buildManifest(\n projectDir: string,\n): Promise<IntegrityManifest> {\n const entries: IntegrityEntry[] = [];\n\n async function scanDir(dir: string, type: IntegrityEntry['type']): Promise<void> {\n let files: string[];\n try {\n files = await readdir(dir);\n } catch {\n return;\n }\n\n for (const file of files) {\n const fullPath = join(dir, file);\n try {\n const fileStat = await stat(fullPath);\n if (fileStat.isFile()) {\n const hash = await hashFile(fullPath);\n if (hash) {\n entries.push({\n filePath: fullPath,\n hash,\n size: fileStat.size,\n createdAt: fileStat.mtime,\n type,\n });\n }\n }\n } catch { /* skip */ }\n }\n }\n\n // Scan known CTO directories\n await scanDir(join(projectDir, 'snapshots'), 'snapshot');\n await scanDir(join(projectDir, 'audit'), 'audit');\n await scanDir(projectDir, 'config');\n\n return {\n version: '2.0',\n createdAt: new Date(),\n entries,\n };\n}\n\nexport async function verifyManifest(\n manifest: IntegrityManifest,\n): Promise<{\n totalFiles: number;\n validFiles: number;\n invalidFiles: string[];\n missingFiles: string[];\n}> {\n const invalidFiles: string[] = [];\n const missingFiles: string[] = [];\n\n for (const entry of manifest.entries) {\n const currentHash = await hashFile(entry.filePath);\n\n if (currentHash === null) {\n missingFiles.push(entry.filePath);\n } else if (currentHash !== entry.hash) {\n invalidFiles.push(entry.filePath);\n }\n }\n\n return {\n totalFiles: manifest.entries.length,\n validFiles: manifest.entries.length - invalidFiles.length - missingFiles.length,\n invalidFiles,\n missingFiles,\n };\n}\n\nexport async function securePermissions(dirPath: string): Promise<number> {\n let count = 0;\n\n try {\n await chmod(dirPath, 0o700);\n count++;\n\n const files = await readdir(dirPath);\n for (const file of files) {\n try {\n const fullPath = join(dirPath, file);\n const fileStat = await stat(fullPath);\n if (fileStat.isFile()) {\n await chmod(fullPath, 0o600);\n count++;\n }\n } catch { /* skip */ }\n }\n } catch { /* dir might not exist */ }\n\n return count;\n}\n"],"mappings":"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","names":["readFile","writeFile","join","createHash","randomUUID","createHash","currentHash","createHash","readFile","chmod","readdir","join","hashContent"]}
|
package/dist/interact/index.js
CHANGED
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
3
3
|
|
|
4
4
|
// src/engine/selector.ts
|
|
5
|
-
import { createHash } from "crypto";
|
|
5
|
+
import { createHash as createHash2 } from "crypto";
|
|
6
6
|
|
|
7
7
|
// src/govern/secrets.ts
|
|
8
8
|
import { readFile } from "fs/promises";
|
|
9
|
-
import {
|
|
9
|
+
import { readFileSync, existsSync, mkdirSync, writeFileSync } from "fs";
|
|
10
|
+
import { resolve, relative, join, dirname } from "path";
|
|
11
|
+
import { createHash } from "crypto";
|
|
10
12
|
var BUILTIN_PATTERNS = [
|
|
11
13
|
// API Keys
|
|
12
14
|
{ type: "api-key", source: `(?:api[_-]?key|apikey)\\s*[:=]\\s*['"]?([a-zA-Z0-9_\\-]{20,})['"]?`, flags: "gi", severity: "critical", description: "API Key" },
|
|
@@ -51,17 +53,46 @@ var BUILTIN_PATTERNS = [
|
|
|
51
53
|
{ type: "api-key", source: "SG\\.[a-zA-Z0-9_-]{22}\\.[a-zA-Z0-9_-]{43}", flags: "g", severity: "critical", description: "SendGrid API Key" },
|
|
52
54
|
// JWT
|
|
53
55
|
{ type: "token", source: "eyJ[a-zA-Z0-9_-]{10,}\\.eyJ[a-zA-Z0-9_-]{10,}\\.[a-zA-Z0-9_-]{10,}", flags: "g", severity: "high", description: "JSON Web Token" },
|
|
56
|
+
// Datadog
|
|
57
|
+
{ type: "api-key", source: `(?:DD_API_KEY|DATADOG_API_KEY)\\s*[:=]\\s*['"]?([a-f0-9]{32})['"]?`, flags: "gi", severity: "critical", description: "Datadog API Key" },
|
|
58
|
+
{ type: "api-key", source: `(?:DD_APP_KEY|DATADOG_APP_KEY)\\s*[:=]\\s*['"]?([a-f0-9]{40})['"]?`, flags: "gi", severity: "critical", description: "Datadog App Key" },
|
|
59
|
+
// Sentry
|
|
60
|
+
{ type: "connection-string", source: "https://[a-f0-9]{32}@[a-z0-9]+\\.ingest\\.sentry\\.io/[0-9]+", flags: "g", severity: "high", description: "Sentry DSN" },
|
|
61
|
+
// Firebase
|
|
62
|
+
{ type: "api-key", source: `(?:FIREBASE_API_KEY|FIREBASE_KEY)\\s*[:=]\\s*['"]?([a-zA-Z0-9_\\-]{30,})['"]?`, flags: "gi", severity: "high", description: "Firebase API Key" },
|
|
63
|
+
{ type: "connection-string", source: `firebase[a-z]*:\\/\\/[^\\s'"]+`, flags: "gi", severity: "high", description: "Firebase URL" },
|
|
64
|
+
// Supabase
|
|
65
|
+
{ type: "api-key", source: "sbp_[a-f0-9]{40}", flags: "g", severity: "critical", description: "Supabase Service Key" },
|
|
66
|
+
{ type: "token", source: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\\.[a-zA-Z0-9_-]{20,}\\.[a-zA-Z0-9_-]{20,}", flags: "g", severity: "high", description: "Supabase Anon/Service JWT" },
|
|
67
|
+
// Vercel
|
|
68
|
+
{ type: "token", source: `(?:VERCEL_TOKEN|VERCEL_API_TOKEN)\\s*[:=]\\s*['"]?([a-zA-Z0-9]{24,})['"]?`, flags: "gi", severity: "critical", description: "Vercel Token" },
|
|
69
|
+
// Heroku
|
|
70
|
+
{ type: "api-key", source: `(?:HEROKU_API_KEY|HEROKU_TOKEN)\\s*[:=]\\s*['"]?([a-f0-9\\-]{36,})['"]?`, flags: "gi", severity: "critical", description: "Heroku API Key" },
|
|
71
|
+
// DigitalOcean
|
|
72
|
+
{ type: "token", source: "dop_v1_[a-f0-9]{64}", flags: "g", severity: "critical", description: "DigitalOcean Personal Access Token" },
|
|
73
|
+
{ type: "token", source: "doo_v1_[a-f0-9]{64}", flags: "g", severity: "critical", description: "DigitalOcean OAuth Token" },
|
|
74
|
+
// Mailgun
|
|
75
|
+
{ type: "api-key", source: "key-[a-zA-Z0-9]{32}", flags: "g", severity: "high", description: "Mailgun API Key" },
|
|
54
76
|
// PII
|
|
55
77
|
{ type: "pii", source: "\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b", flags: "g", severity: "medium", description: "Email Address (PII)" },
|
|
56
|
-
{ type: "pii", source: "\\b\\d{3}[-.]
|
|
78
|
+
{ type: "pii", source: "\\b(?!000|666|9\\d{2})(\\d{3})[-.]?(?!00)(\\d{2})[-.]?(?!0000)(\\d{4})\\b", flags: "g", severity: "high", description: "Possible SSN (PII)" }
|
|
57
79
|
];
|
|
80
|
+
var _cachedBuiltinPatterns = null;
|
|
81
|
+
function getBuiltinPatterns() {
|
|
82
|
+
if (!_cachedBuiltinPatterns) {
|
|
83
|
+
_cachedBuiltinPatterns = BUILTIN_PATTERNS.map((def) => ({
|
|
84
|
+
type: def.type,
|
|
85
|
+
pattern: new RegExp(def.source, def.flags),
|
|
86
|
+
severity: def.severity,
|
|
87
|
+
description: def.description
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
return _cachedBuiltinPatterns;
|
|
91
|
+
}
|
|
58
92
|
function buildPatterns(customPatterns = []) {
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
severity: def.severity,
|
|
63
|
-
description: def.description
|
|
64
|
-
}));
|
|
93
|
+
const builtins = getBuiltinPatterns();
|
|
94
|
+
if (customPatterns.length === 0) return builtins;
|
|
95
|
+
const patterns = [...builtins];
|
|
65
96
|
for (const custom of customPatterns) {
|
|
66
97
|
try {
|
|
67
98
|
patterns.push({
|
|
@@ -75,7 +106,7 @@ function buildPatterns(customPatterns = []) {
|
|
|
75
106
|
}
|
|
76
107
|
return patterns;
|
|
77
108
|
}
|
|
78
|
-
function scanContentForSecrets(content, filePath, customPatterns = []) {
|
|
109
|
+
function scanContentForSecrets(content, filePath, customPatterns = [], extraPiiSafeDomains) {
|
|
79
110
|
const findings = [];
|
|
80
111
|
const lines = content.split("\n");
|
|
81
112
|
const allPatterns = buildPatterns(customPatterns);
|
|
@@ -87,6 +118,7 @@ function scanContentForSecrets(content, filePath, customPatterns = []) {
|
|
|
87
118
|
while ((match = secretPattern.pattern.exec(line)) !== null) {
|
|
88
119
|
const matchText = match[0];
|
|
89
120
|
if (isTemplateOrPlaceholder(matchText)) continue;
|
|
121
|
+
if (secretPattern.type === "pii" && isSafeEmail(matchText, extraPiiSafeDomains)) continue;
|
|
90
122
|
findings.push({
|
|
91
123
|
type: secretPattern.type,
|
|
92
124
|
file: filePath,
|
|
@@ -134,6 +166,36 @@ function isTemplateOrPlaceholder(value) {
|
|
|
134
166
|
];
|
|
135
167
|
return placeholders.some((p) => p.test(value));
|
|
136
168
|
}
|
|
169
|
+
var PII_SAFE_EMAIL_DOMAINS = /* @__PURE__ */ new Set([
|
|
170
|
+
"example.com",
|
|
171
|
+
"example.org",
|
|
172
|
+
"example.net",
|
|
173
|
+
"test.com",
|
|
174
|
+
"test.org",
|
|
175
|
+
"test.net",
|
|
176
|
+
"localhost",
|
|
177
|
+
"localhost.localdomain",
|
|
178
|
+
"email.com",
|
|
179
|
+
"mail.com",
|
|
180
|
+
"foo.com",
|
|
181
|
+
"bar.com",
|
|
182
|
+
"baz.com",
|
|
183
|
+
"acme.com",
|
|
184
|
+
"company.com",
|
|
185
|
+
"corp.com",
|
|
186
|
+
"noreply.com",
|
|
187
|
+
"no-reply.com",
|
|
188
|
+
"users.noreply.github.com",
|
|
189
|
+
"placeholder.com"
|
|
190
|
+
]);
|
|
191
|
+
function isSafeEmail(value, extraDomains) {
|
|
192
|
+
const match = value.match(/@([a-zA-Z0-9.-]+)$/);
|
|
193
|
+
if (!match) return false;
|
|
194
|
+
const domain = match[1].toLowerCase();
|
|
195
|
+
if (PII_SAFE_EMAIL_DOMAINS.has(domain)) return true;
|
|
196
|
+
if (extraDomains && extraDomains.has(domain)) return true;
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
137
199
|
function deduplicateFindings(findings) {
|
|
138
200
|
const seen = /* @__PURE__ */ new Set();
|
|
139
201
|
return findings.filter((f) => {
|
|
@@ -147,8 +209,8 @@ function deduplicateFindings(findings) {
|
|
|
147
209
|
// src/engine/pruner.ts
|
|
148
210
|
import { Project, SyntaxKind } from "ts-morph";
|
|
149
211
|
import { readFile as readFile3 } from "fs/promises";
|
|
150
|
-
import { existsSync } from "fs";
|
|
151
|
-
import { join } from "path";
|
|
212
|
+
import { existsSync as existsSync2 } from "fs";
|
|
213
|
+
import { join as join2 } from "path";
|
|
152
214
|
|
|
153
215
|
// src/engine/tokenizer.ts
|
|
154
216
|
import { encodingForModel } from "js-tiktoken";
|
|
@@ -404,9 +466,9 @@ function addJSDoc(node, parts) {
|
|
|
404
466
|
function findTsConfig(filePath) {
|
|
405
467
|
let dir = filePath;
|
|
406
468
|
for (let i = 0; i < 10; i++) {
|
|
407
|
-
dir =
|
|
408
|
-
const candidate =
|
|
409
|
-
if (
|
|
469
|
+
dir = join2(dir, "..");
|
|
470
|
+
const candidate = join2(dir, "tsconfig.json");
|
|
471
|
+
if (existsSync2(candidate)) return candidate;
|
|
410
472
|
}
|
|
411
473
|
return void 0;
|
|
412
474
|
}
|
|
@@ -673,7 +735,7 @@ async function selectContext(input) {
|
|
|
673
735
|
);
|
|
674
736
|
const excludedRisk = excludedFiles.length > 0 ? Math.round(excludedFiles.reduce((s, f) => s + f.riskScore, 0) / excludedFiles.length) : 0;
|
|
675
737
|
const hashInput = selectedFiles.map((f) => `${f.relativePath}:${f.pruneLevel}`).sort().join("|") + `|budget:${budget}`;
|
|
676
|
-
const hash =
|
|
738
|
+
const hash = createHash2("sha256").update(hashInput).digest("hex").substring(0, 16);
|
|
677
739
|
return {
|
|
678
740
|
files: selectedFiles,
|
|
679
741
|
totalTokens: usedTokens,
|
|
@@ -1166,20 +1228,20 @@ function makeSection(id, role, content) {
|
|
|
1166
1228
|
}
|
|
1167
1229
|
|
|
1168
1230
|
// src/govern/audit.ts
|
|
1169
|
-
import { randomUUID, createHash as
|
|
1231
|
+
import { randomUUID, createHash as createHash3 } from "crypto";
|
|
1170
1232
|
import { readdir, chmod } from "fs/promises";
|
|
1171
|
-
import { join as
|
|
1233
|
+
import { join as join3 } from "path";
|
|
1172
1234
|
import { userInfo } from "os";
|
|
1173
1235
|
import { homedir } from "os";
|
|
1174
1236
|
var CTO_DIR = ".cto-ai";
|
|
1175
1237
|
var AUDIT_DIR = "audit";
|
|
1176
1238
|
var MAX_ENTRIES_PER_FILE = 500;
|
|
1177
1239
|
function getAuditDir() {
|
|
1178
|
-
return
|
|
1240
|
+
return join3(homedir(), CTO_DIR, AUDIT_DIR);
|
|
1179
1241
|
}
|
|
1180
1242
|
function getCurrentAuditFile() {
|
|
1181
1243
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0].replace(/-/g, "");
|
|
1182
|
-
return
|
|
1244
|
+
return join3(getAuditDir(), `audit_${date}.json`);
|
|
1183
1245
|
}
|
|
1184
1246
|
function computeIntegrityHash(entry) {
|
|
1185
1247
|
const payload = JSON.stringify({
|
|
@@ -1190,7 +1252,7 @@ function computeIntegrityHash(entry) {
|
|
|
1190
1252
|
projectPath: entry.projectPath,
|
|
1191
1253
|
details: entry.details
|
|
1192
1254
|
});
|
|
1193
|
-
return
|
|
1255
|
+
return createHash3("sha256").update(payload).digest("hex");
|
|
1194
1256
|
}
|
|
1195
1257
|
async function ensureDir(dirPath) {
|
|
1196
1258
|
const { mkdir } = await import("fs/promises");
|
|
@@ -1206,9 +1268,9 @@ async function readJSON(filePath) {
|
|
|
1206
1268
|
}
|
|
1207
1269
|
}
|
|
1208
1270
|
async function writeJSON(filePath, data) {
|
|
1209
|
-
const { writeFile } = await import("fs/promises");
|
|
1210
|
-
await ensureDir(
|
|
1211
|
-
await
|
|
1271
|
+
const { writeFile: writeFile2 } = await import("fs/promises");
|
|
1272
|
+
await ensureDir(join3(filePath, ".."));
|
|
1273
|
+
await writeFile2(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
1212
1274
|
}
|
|
1213
1275
|
async function logAudit(action, projectPath, details = {}) {
|
|
1214
1276
|
const auditDir = getAuditDir();
|