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>
136 lines
4.7 KiB
JavaScript
Executable file
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();
|