Adds comprehensive protection against exposing internal implementation details in public-facing documentation. New Governance Rule (inst_084): - Quadrant: SYSTEM - Persistence: HIGH - Scope: Public documents (confidential:false) - Enforcement: Pre-commit hooks (mandatory) Implementation: 1. attack-surface-validator.util.js - Pattern detection for file paths, API endpoints, admin URLs, ports - Frontmatter parsing (respects confidential:true exemption) - Code block exemption (doesn't flag technical examples) - Intelligent line numbering for violation reporting 2. check-attack-surface.js - Pre-commit script that scans staged documents - User-friendly violation reporting with suggestions - Integration with git workflow 3. Pre-commit hook integration - Added as Check #3 in git hooks - Runs after prohibited terms, before test requirements - Blocks commits with attack surface exposures Detection Patterns: ✅ File paths: src/*, public/*, scripts/* ✅ API endpoints: /api/*, /admin/* ✅ File naming patterns: *.util.js, *.service.js ✅ Port numbers in prose ✅ Connection strings Exemptions: - Code blocks (```) - Inline code (`) - Confidential documents (confidential:true) - Internal technical documentation Security Rationale (Defense-in-Depth): - Prevents reconnaissance by obscuring architecture - Reduces attack surface by hiding implementation paths - Complements credential protection (inst_069/070) - Part of layered security strategy (inst_072) Testing: - Validated against test document with known exposures - 7 violations detected correctly - Code block exemption verified - All expected pattern types detected Example Violations Blocked: ❌ "Dashboard at /admin/audit-analytics.html" ✅ "Administrative Dashboard" ❌ "GET /api/admin/audit-logs endpoint" ✅ "Authenticated API for audit data" ❌ "In activity-classifier.util.js" ✅ "The activity classifier" This enforcement prevented the exact security issue discovered in governance-bi-tools.md which exposed admin paths and API endpoints. Also fixed prohibited terms checker to exempt instruction-history.json (which contains prohibited term DEFINITIONS, not violations). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
144 lines
4.3 KiB
JavaScript
Executable file
144 lines
4.3 KiB
JavaScript
Executable file
#!/usr/bin/env node
|
|
|
|
/*
|
|
* Copyright 2025 John G Stroh
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
/**
|
|
* Pre-commit Attack Surface Check (inst_084)
|
|
*
|
|
* Scans staged files for attack surface exposures
|
|
* Blocks commits that expose internal implementation details in public documents
|
|
*/
|
|
|
|
const { execSync } = require('child_process');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const { validateFile } = require('../src/utils/attack-surface-validator.util');
|
|
|
|
function getStagedFiles() {
|
|
try {
|
|
const output = execSync('git diff --cached --name-only --diff-filter=ACM', {
|
|
encoding: 'utf8',
|
|
stdio: ['pipe', 'pipe', 'ignore']
|
|
});
|
|
|
|
return output.split('\n').filter(f => f.trim());
|
|
} catch (error) {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
function checkStagedFiles() {
|
|
const stagedFiles = getStagedFiles();
|
|
|
|
if (stagedFiles.length === 0) {
|
|
console.log('✅ No attack surface exposure check needed (no staged files)');
|
|
return { success: true, exposures: [] };
|
|
}
|
|
|
|
// Filter for documents only
|
|
const documentFiles = stagedFiles.filter(f =>
|
|
f.endsWith('.md') || f.includes('/docs/')
|
|
);
|
|
|
|
if (documentFiles.length === 0) {
|
|
console.log('✅ No documents in staged files');
|
|
return { success: true, exposures: [] };
|
|
}
|
|
|
|
console.log(`\n🔍 Scanning ${documentFiles.length} document(s) for attack surface exposure...`);
|
|
|
|
const allExposures = [];
|
|
|
|
for (const file of documentFiles) {
|
|
const filePath = path.join(process.cwd(), file);
|
|
|
|
if (!fs.existsSync(filePath)) {
|
|
continue; // File was deleted
|
|
}
|
|
|
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
const result = validateFile(file, content);
|
|
|
|
if (!result.allowed) {
|
|
allExposures.push({
|
|
file,
|
|
violations: result.violations
|
|
});
|
|
}
|
|
}
|
|
|
|
return {
|
|
success: allExposures.length === 0,
|
|
exposures: allExposures
|
|
};
|
|
}
|
|
|
|
function printExposures(exposures) {
|
|
console.log('\n' + '━'.repeat(80));
|
|
console.log('❌ ATTACK SURFACE EXPOSURE DETECTED (inst_084)');
|
|
console.log('━'.repeat(80));
|
|
console.log('');
|
|
console.log('The following files expose internal implementation details:');
|
|
console.log('');
|
|
|
|
for (const { file, violations } of exposures) {
|
|
console.log(`\n🔴 ${file}`);
|
|
console.log('');
|
|
|
|
for (const violation of violations) {
|
|
console.log(` Line ${violation.line}: ${violation.description}`);
|
|
console.log(` Found: "${violation.match}"`);
|
|
console.log(` 💡 ${violation.suggestion}`);
|
|
console.log('');
|
|
}
|
|
}
|
|
|
|
console.log('━'.repeat(80));
|
|
console.log('⚠️ SECURITY RISK: Internal architecture exposed in public documents');
|
|
console.log('━'.repeat(80));
|
|
console.log('');
|
|
console.log('Why this matters:');
|
|
console.log(' • Exact file paths → easier reconnaissance for attackers');
|
|
console.log(' • API endpoints → attack surface mapping');
|
|
console.log(' • Port numbers → network scanning targets');
|
|
console.log(' • Internal URLs → direct access attempts');
|
|
console.log('');
|
|
console.log('Fix: Use generalized component names instead of specific paths');
|
|
console.log('');
|
|
console.log('Examples:');
|
|
console.log(' ❌ "Dashboard at /admin/audit-analytics.html"');
|
|
console.log(' ✅ "Administrative Dashboard"');
|
|
console.log(' ❌ "GET /api/admin/audit-logs endpoint"');
|
|
console.log(' ✅ "Authenticated API for audit data"');
|
|
console.log('');
|
|
console.log('To bypass (NOT RECOMMENDED):');
|
|
console.log(' git commit --no-verify');
|
|
console.log('');
|
|
}
|
|
|
|
// Main execution
|
|
const result = checkStagedFiles();
|
|
|
|
if (!result.success) {
|
|
printExposures(result.exposures);
|
|
process.exit(1);
|
|
} else {
|
|
if (result.exposures.length === 0) {
|
|
console.log('✅ No attack surface exposures detected');
|
|
}
|
|
process.exit(0);
|
|
}
|