tractatus/scripts/fix-admin-csp-violations.js
TheFlow 2298d36bed fix(submissions): restructure Economist package and fix article display
- Create Economist SubmissionTracking package correctly:
  * mainArticle = full blog post content
  * coverLetter = 216-word SIR— letter
  * Links to blog post via blogPostId
- Archive 'Letter to The Economist' from blog posts (it's the cover letter)
- Fix date display on article cards (use published_at)
- Target publication already displaying via blue badge

Database changes:
- Make blogPostId optional in SubmissionTracking model
- Economist package ID: 68fa85ae49d4900e7f2ecd83
- Le Monde package ID: 68fa2abd2e6acd5691932150

Next: Enhanced modal with tabs, validation, export

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-24 08:47:42 +13:00

149 lines
5.5 KiB
JavaScript
Raw Permalink 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');