DATABASE SYNC INFRASTRUCTURE:
- scripts/sync-instructions-to-db.js
- Syncs .claude/instruction-history.json to MongoDB governanceRules collection
- Handles inserts, updates, and deactivations
- Validates file and database counts match
- Used in governance audit (54 → 56 → 59 active rules)
- Required for production deployment of governance rules
CSP COMPLIANCE CHECKING:
- scripts/check-csp-violations.js
- Enforces Content Security Policy compliance (inst_008)
- Checks staged HTML files for:
- Inline scripts (<script> tags with code)
- Inline event handlers (onclick, onload, etc.)
- Inline styles (style attributes)
- Integrated with .git/hooks/pre-commit
- Blocks commits with CSP violations
REASON FOR CREATION:
- sync-instructions-to-db.js: Needed to deploy governance rules to production
- check-csp-violations.js: Pre-commit hook was calling missing script
USAGE:
- Sync to DB: node scripts/sync-instructions-to-db.js
- CSP check: Runs automatically on git commit (via pre-commit hook)
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
113 lines
3.1 KiB
JavaScript
Executable file
113 lines
3.1 KiB
JavaScript
Executable file
#!/usr/bin/env node
|
|
|
|
/**
|
|
* CSP Violations Checker
|
|
* Enforces Content Security Policy compliance (inst_008)
|
|
*
|
|
* Checks staged files for:
|
|
* - Inline scripts (<script> tags with code)
|
|
* - Inline event handlers (onclick, onload, etc.)
|
|
* - Inline styles in HTML
|
|
*
|
|
* Does NOT check:
|
|
* - Non-HTML files (JS, CSS, MD, etc.)
|
|
* - <script src="..."> external scripts
|
|
*/
|
|
|
|
const { execSync } = require('child_process');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
// Get list of staged files
|
|
let stagedFiles;
|
|
try {
|
|
stagedFiles = execSync('git diff --cached --name-only --diff-filter=ACMR', { encoding: 'utf8' })
|
|
.split('\n')
|
|
.filter(f => f.trim() !== '');
|
|
} catch (error) {
|
|
console.error('Error getting staged files:', error.message);
|
|
process.exit(0); // Allow commit if can't check
|
|
}
|
|
|
|
// Filter to HTML files only
|
|
const htmlFiles = stagedFiles.filter(f => f.endsWith('.html'));
|
|
|
|
if (htmlFiles.length === 0) {
|
|
// No HTML files, nothing to check
|
|
process.exit(0);
|
|
}
|
|
|
|
let violations = [];
|
|
|
|
// Check each HTML file
|
|
htmlFiles.forEach(file => {
|
|
const filePath = path.join(process.cwd(), file);
|
|
|
|
if (!fs.existsSync(filePath)) {
|
|
return; // File deleted, skip
|
|
}
|
|
|
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
const lines = content.split('\n');
|
|
|
|
lines.forEach((line, index) => {
|
|
const lineNum = index + 1;
|
|
|
|
// Check for inline scripts (but not <script src="...">)
|
|
if (/<script(?!.*src=)[^>]*>[\s\S]*?<\/script>/i.test(line)) {
|
|
if (line.includes('<script>') || (line.includes('<script ') && !line.includes('src='))) {
|
|
violations.push({
|
|
file,
|
|
line: lineNum,
|
|
type: 'inline-script',
|
|
content: line.trim().substring(0, 80)
|
|
});
|
|
}
|
|
}
|
|
|
|
// Check for inline event handlers
|
|
const inlineHandlers = ['onclick', 'onload', 'onmouseover', 'onsubmit', 'onerror', 'onchange'];
|
|
inlineHandlers.forEach(handler => {
|
|
if (new RegExp(`\\s${handler}=`, 'i').test(line)) {
|
|
violations.push({
|
|
file,
|
|
line: lineNum,
|
|
type: 'inline-handler',
|
|
handler,
|
|
content: line.trim().substring(0, 80)
|
|
});
|
|
}
|
|
});
|
|
|
|
// Check for inline styles (style attribute)
|
|
if (/\sstyle\s*=\s*["'][^"']*["']/i.test(line)) {
|
|
// Allow Tailwind utility classes pattern (common false positive)
|
|
if (!line.includes('class=') || line.match(/style\s*=\s*["'][^"']{20,}/)) {
|
|
violations.push({
|
|
file,
|
|
line: lineNum,
|
|
type: 'inline-style',
|
|
content: line.trim().substring(0, 80)
|
|
});
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
if (violations.length === 0) {
|
|
process.exit(0); // No violations, allow commit
|
|
}
|
|
|
|
// Report violations
|
|
console.error('\nCSP Violations Found:\n');
|
|
violations.forEach(v => {
|
|
console.error(` ${v.file}:${v.line}`);
|
|
console.error(` Type: ${v.type}${v.handler ? ' (' + v.handler + ')' : ''}`);
|
|
console.error(` Content: ${v.content}`);
|
|
console.error('');
|
|
});
|
|
|
|
console.error(`Total violations: ${violations.length}\n`);
|
|
console.error('Fix these violations or use --no-verify to bypass (not recommended)\n');
|
|
|
|
process.exit(1); // Block commit
|