tractatus/scripts/fix-admin-csp-violations.js
TheFlow 0e6be3eaf1 refactor: remove website code and fix critical startup crashes (Phase 8)
CRITICAL FIX: Server would CRASH ON STARTUP (multiple import errors)

REMOVED (2 scripts):
1. scripts/framework-watchdog.js
   - Monitored .claude/session-state.json (OUR Claude Code setup)
   - Monitored .claude/token-checkpoints.json (OUR file structure)
   - Implementers won't have our .claude/ directory

2. scripts/init-db.js
   - Created website collections: blog_posts, media_inquiries, case_submissions
   - Created website collections: resources, moderation_queue, users, citations
   - Created website collections: translations, koha_donations
   - Next steps referenced deleted scripts (npm run seed:admin)

REWRITTEN (2 files):

src/models/index.js (29 lines → 27 lines)
- REMOVED imports: Document, BlogPost, MediaInquiry, CaseSubmission, Resource
- REMOVED imports: ModerationQueue, User (all deleted in Phase 2)
- KEPT imports: AuditLog, DeliberationSession, GovernanceLog, GovernanceRule
- KEPT imports: Precedent, Project, SessionState, VariableValue, VerificationLog
- Result: Only framework models exported

src/server.js (284 lines → 163 lines, 43% reduction)
- REMOVED: Imports to deleted middleware (csrf-protection, response-sanitization)
- REMOVED: Stripe webhook handling (/api/koha/webhook)
- REMOVED: Static file caching (for deleted public/ directory)
- REMOVED: Static file serving (public/ deleted in Phase 6)
- REMOVED: CSRF token endpoint
- REMOVED: Website homepage with "auth, documents, blog, admin" references
- REMOVED: Instruction sync (scripts/sync-instructions-to-db.js reference)
- REMOVED: Hardcoded log path (${process.env.HOME}/var/log/tractatus/...)
- REMOVED: Website-specific security middleware
- KEPT: Security headers, rate limiting, CORS, body parsers
- KEPT: API routes, governance services, MongoDB connections
- RESULT: Clean framework-only server

RESULT: Repository can now start without crashes, all imports resolve

🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-21 22:17:02 +13:00

149 lines
5.5 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
/**
* Fix CSP violations in admin JS files
* - Replace inline styles with classes and data attributes
* - Replace inline event handlers with event delegation
*/
const fs = require('fs');
const path = require('path');
// Fix auth-check.js inline styles
function fixAuthCheck() {
const filePath = path.join(__dirname, '../public/js/admin/auth-check.js');
let content = fs.readFileSync(filePath, 'utf8');
const oldHTML = ` <div style="display: flex; align-items: center; justify-content: center; height: 100vh; font-family: system-ui, -apple-system, sans-serif;">
<div style="text-align: center;">
<svg style="width: 64px; height: 64px; margin: 0 auto 16px; color: #3B82F6;" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/>
</svg>
<h2 style="font-size: 20px; font-weight: 600; color: #111827; margin-bottom: 8px;">Authentication Required</h2>
<p style="color: #6B7280; margin-bottom: 16px;">\${reason}</p>
<p style="color: #9CA3AF; font-size: 14px;">Redirecting to login...</p>`;
const newHTML = ` <div class="auth-error-container">
<div class="auth-error-content">
<svg class="auth-error-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/>
</svg>
<h2 class="auth-error-title">Authentication Required</h2>
<p class="auth-error-message">\${reason}</p>
<p class="auth-error-redirect">Redirecting to login...</p>`;
if (content.includes(oldHTML)) {
content = content.replace(oldHTML, newHTML);
fs.writeFileSync(filePath, content);
return 6; // 6 inline styles fixed
}
return 0;
}
// Fix progress bar widths by using data attributes
function fixProgressBars() {
const files = [
'public/js/admin/audit-analytics.js',
'public/js/admin/rule-editor.js',
'public/js/admin/rule-manager.js'
];
let totalFixed = 0;
files.forEach(file => {
const filePath = path.join(__dirname, '..', file);
let content = fs.readFileSync(filePath, 'utf8');
let fileFixed = 0;
// Pattern 1: <div ... style="width: ${var}%"></div>
const pattern1 = /(<div[^>]*class="[^"]*(?:bg-blue-600|bg-green-500|bg-blue-500|bg-yellow-500)[^"]*"[^>]*)\s+style="width:\s*\$\{([^}]+)\}%"/g;
content = content.replace(pattern1, (match, before, variable) => {
fileFixed++;
return `${before} data-width="\${${variable}}"`;
});
// Pattern 2: <div ... style="width: 0%"> or style="width: 100%">
const pattern2 = /(<div[^>]*(?:id="[^"]*-bar")[^>]*)\s+style="width:\s*(0|100)%"/g;
content = content.replace(pattern2, (match, before, value) => {
fileFixed++;
return `${before} data-width="${value}"`;
});
// Pattern 3: style="height: ${barHeight}%"
const pattern3 = /style="height:\s*\$\{([^}]+)\}%"/g;
content = content.replace(pattern3, (match, variable) => {
fileFixed++;
return `data-height="\${${variable}}"`;
});
if (fileFixed > 0) {
fs.writeFileSync(filePath, content);
console.log(`${file}: Fixed ${fileFixed} inline style(s)`);
totalFixed += fileFixed;
}
});
return totalFixed;
}
// Add width-setting helper after DOM insertion
function addProgressBarHelper() {
const files = [
{ file: 'public/js/admin/audit-analytics.js', hasProgressBars: true },
{ file: 'public/js/admin/rule-editor.js', hasProgressBars: true },
{ file: 'public/js/admin/rule-manager.js', hasProgressBars: true }
];
const helper = `
// Set widths from data attributes (CSP compliance)
function setProgressBarWidths(container) {
const elements = container.querySelectorAll('[data-width], [data-height]');
elements.forEach(el => {
if (el.dataset.width) {
el.style.width = el.dataset.width + '%';
}
if (el.dataset.height) {
el.style.height = el.dataset.height + '%';
}
});
}`;
files.forEach(({ file, hasProgressBars }) => {
if (!hasProgressBars) return;
const filePath = path.join(__dirname, '..', file);
let content = fs.readFileSync(filePath, 'utf8');
// Check if helper already exists
if (content.includes('setProgressBarWidths')) {
return;
}
// Add helper function before the last closing brace/parenthesis of the file
// Find a good insertion point - typically after other helper functions
const insertionPoint = content.lastIndexOf('})()');
if (insertionPoint > 0) {
content = content.slice(0, insertionPoint) + helper + '\n\n' + content.slice(insertionPoint);
fs.writeFileSync(filePath, content);
console.log(`${file}: Added setProgressBarWidths helper`);
}
});
}
// Main execution
console.log('\n🔧 Fixing admin CSP violations...\n');
let totalFixed = 0;
console.log('1. Fixing auth-check.js inline styles...');
totalFixed += fixAuthCheck();
console.log('\n2. Converting progress bar widths to data attributes...');
totalFixed += fixProgressBars();
console.log('\n3. Adding progress bar width helpers...');
addProgressBarHelper();
console.log(`\n✅ Total inline styles fixed: ${totalFixed}`);
console.log('\n⚠ Note: Inline event handlers require manual refactoring');
console.log(' Run scripts/fix-admin-event-handlers.js for those.\n');