tractatus/scripts/check-credential-exposure.js
TheFlow 65b2c80be3 feat(governance): second wave enforcement - 64% improvement (28% → 46%)
Implements 7 additional architectural enforcement mechanisms:

 Prohibited Terms Detection (inst_016/017/018):
- scripts/check-prohibited-terms.js
- Scans for absolute assurance terms ("guarantee", "100% secure")
- Detects maturity claims without evidence ("production-ready", "battle-tested")
- Checks statistics require citation or [NEEDS VERIFICATION]
- Integrated into .git/hooks/pre-commit (Check 2)

 Credential Exposure Prevention (inst_069/070):
- scripts/check-credential-exposure.js
- Detects real API keys, secrets, passwords in documentation
- Validates example credentials use proper patterns (EXAMPLE/REDACTED)
- CRITICAL: Runs first in pre-commit (Check 0)

 Confidential Document Protection (inst_012/015):
- scripts/check-confidential-docs.js
- Prevents deployment of internal/session-handoff documents
- Scans filenames and content for [CONFIDENTIAL]/[INTERNAL] markers
- Integrated into scripts/deploy.sh pre-flight checks

 Enhanced Pre-Commit Hook:
Now runs 4 checks in order:
0. Credential exposure (CRITICAL)
1. CSP compliance
2. Prohibited terms
3. Test requirements

 Enhanced Deployment Script:
- Added confidential document check to deploy.sh
- Scans public/ and docs/ before deployment
- Blocks deployment if confidential markers found

 Updated Enforcement Map:
- Added all new mechanisms to audit-enforcement.js
- Updated inst_008_CONSOLIDATED mapping
- New mappings: inst_012, inst_015, inst_016, inst_017, inst_018, inst_069, inst_070

📊 Enforcement Progress:
- Wave 1: 11/39 imperative instructions enforced (28%)
- Wave 2: 18/39 imperative instructions enforced (46%)
- Improvement: +7 instructions = +64% increase
- Remaining gaps: 21/39 (54%)

🎯 Next Priority Gaps:
- inst_013/043/045: API security validation
- inst_019: Context pressure comprehensive accounting
- inst_025: Deployment file mapping
- inst_039/040: Batch operation verification
- inst_079/080/081: Values/principles (process-based)

🔒 Security Posture:
- CRITICAL security checks now run first (credential exposure)
- All text files scanned before commit
- All deployment candidates scanned before rsync

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 13:26:33 +13:00

136 lines
4.7 KiB
JavaScript
Executable file

#!/usr/bin/env node
/**
* Credential Exposure Scanner - Enforces inst_069, inst_070
* Detects real credentials in documentation and code
*/
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
// Patterns for real credentials (not example/redacted)
const CREDENTIAL_PATTERNS = [
// API keys that look real (not EXAMPLE/REDACTED)
{ pattern: /sk-ant-api03-[A-Za-z0-9_-]{95,}(?!EXAMPLE|REDACTED)/g, type: 'Anthropic API Key' },
{ pattern: /sk-[a-z0-9]{32,}(?!EXAMPLE|REDACTED|your-key)/gi, type: 'Stripe Secret Key' },
{ pattern: /pk-[a-z0-9]{32,}(?!EXAMPLE|REDACTED|your-key)/gi, type: 'Stripe Public Key' },
// Generic patterns that look suspicious
{ pattern: /api[_-]?key[\s:=]+["']?([a-z0-9]{32,})["']?(?!EXAMPLE|REDACTED|your-|xxx)/gi, type: 'Generic API Key' },
{ pattern: /secret[\s:=]+["']?([a-z0-9]{32,})["']?(?!EXAMPLE|REDACTED|your-|xxx)/gi, type: 'Generic Secret' },
{ pattern: /password[\s:=]+["']?([^"'\s]{8,})["']?(?!REDACTED|your-|xxx|example|password123)/gi, type: 'Possible Password' },
// AWS
{ pattern: /AKIA[0-9A-Z]{16}(?!EXAMPLE)/g, type: 'AWS Access Key' },
// JWT tokens (look for proper structure, not examples)
{ pattern: /eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}(?!EXAMPLE)/g, type: 'JWT Token' },
// Database connection strings with real passwords
{ pattern: /mongodb:\/\/[^:]+:([^@\s]{8,})@(?!REDACTED|your-password|example)/gi, type: 'MongoDB Credential' },
{ pattern: /postgres:\/\/[^:]+:([^@\s]{8,})@(?!REDACTED|your-password|example)/gi, type: 'PostgreSQL Credential' }
];
function checkFile(filePath) {
const content = fs.readFileSync(filePath, 'utf8');
const lines = content.split('\n');
const findings = [];
lines.forEach((line, idx) => {
CREDENTIAL_PATTERNS.forEach(({ pattern, type }) => {
const matches = line.matchAll(pattern);
for (const match of matches) {
findings.push({
file: filePath,
line: idx + 1,
type,
text: line.trim(),
match: match[0]
});
}
});
});
return findings;
}
function scanFiles(files) {
const allFindings = [];
files.forEach(file => {
if (!fs.existsSync(file)) return;
// Skip binary files, node_modules, etc.
if (file.includes('node_modules') || file.includes('.git/') || file.includes('dist/')) return;
const ext = path.extname(file).toLowerCase();
if (['.png', '.jpg', '.jpeg', '.gif', '.pdf', '.zip'].includes(ext)) return;
try {
const findings = checkFile(file);
allFindings.push(...findings);
} catch (err) {
// Binary file or encoding issue - skip
}
});
return allFindings;
}
function main() {
const args = process.argv.slice(2);
let files = [];
if (args.length === 0) {
// Scan staged git files
try {
const staged = execSync('git diff --cached --name-only --diff-filter=ACM', {
encoding: 'utf8'
});
files = staged.trim().split('\n').filter(f => f.length > 0);
} catch (err) {
console.log('⚠️ Not in git repository - skipping credential scan');
process.exit(0);
}
} else {
files = args;
}
if (files.length === 0) {
console.log('✅ No files to scan for credentials');
process.exit(0);
}
console.log(`\n🔍 Scanning ${files.length} file(s) for credential exposure...\n`);
const findings = scanFiles(files);
if (findings.length === 0) {
console.log('✅ No credentials detected\n');
process.exit(0);
}
// Report findings
console.log(`❌ Found ${findings.length} potential credential(s):\n`);
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
findings.forEach(f => {
console.log(`🔴 ${f.file}:${f.line}`);
console.log(` Type: ${f.type}`);
console.log(` Match: ${f.match.substring(0, 50)}${f.match.length > 50 ? '...' : ''}`);
console.log(` Line: ${f.text.substring(0, 80)}${f.text.length > 80 ? '...' : ''}`);
console.log('');
});
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
console.log('⚠️ CRITICAL SECURITY ISSUE (inst_069/inst_070)\n');
console.log('Replace real credentials with:');
console.log(' • API keys: "sk-ant-api03-EXAMPLE-REDACTED-NEVER-USE"');
console.log(' • Secrets: "REDACTED" or "your-secret-here"');
console.log(' • Passwords: "your-password-here"\n');
console.log('Use environment variables or secret management for real credentials.\n');
process.exit(1);
}
main();