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>
149 lines
5.5 KiB
JavaScript
149 lines
5.5 KiB
JavaScript
#!/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');
|