Correlation Window
Window Size: ${metaData.time_window_value || 'N/A'} ${metaData.time_window_unit || 'N/A'}
Aggregate Key: ${metaData.aggregate_by || 'N/A'} (Must be equal across all correlating events)
Tuning Notes: ${metaData.rule_notes || 'N/A'}
Event Conditions (ALL Must Occur - AND Logic)
| ID |
Event Name |
Condition |
Min Count |
Role/Description |
`;
if (eventConditions.length > 0) {
eventConditions.forEach((event, index) => {
html += `
| E-${index + 1} |
${event.name} |
${event.field} ${event.operator} ${event.value} |
${event.minCount} |
${event.description} |
`;
});
} else {
html += `| No correlation events defined. Rule will not fire. |
`;
}
html += `
`;
reviewContent.innerHTML = html;
pdfDownloadBtn.disabled = eventConditions.length === 0;
switchTab(2); // Switch to review tab
};
/**
* PDF Generation Function
*/
const downloadPDF = () => {
const metaData = getMetaFormData();
const jsPDF = window.jspdf.jsPDF; // Correctly access the constructor
const doc = new jsPDF('p', 'pt', 'a4');
let currentY = 40;
const margin = 40;
const pageWidth = doc.internal.pageSize.width;
const maxWidth = pageWidth - (margin * 2);
const getSeverityColor = (severity) => {
switch (severity) {
case 'Critical': return [244, 67, 54]; // Danger
case 'High': return [255, 152, 0]; // Warning
case 'Medium': return [0, 188, 212]; // Info
default: return [46, 139, 87]; // Primary/Success
}
};
const severityColor = getSeverityColor(metaData.rule_severity);
const checkPageBreak = (spaceNeeded) => {
if (currentY + spaceNeeded > doc.internal.pageSize.height - margin) {
doc.addPage();
currentY = margin;
}
};
const addSectionHeader = (title, color = [0, 0, 0]) => {
checkPageBreak(25);
doc.setFontSize(16);
doc.setFont('Helvetica', 'bold');
doc.setTextColor(color[0], color[1], color[2]);
doc.text(title, margin, currentY);
currentY += 12;
doc.setLineWidth(0.5);
doc.setDrawColor(200);
doc.line(margin, currentY, pageWidth - margin, currentY);
currentY += 15;
doc.setTextColor(0);
};
const addText = (text, size = 11, style = 'normal', indent = 0) => {
doc.setFontSize(size);
doc.setFont('Helvetica', style);
const lines = doc.splitTextToSize(text, maxWidth - indent);
checkPageBreak(lines.length * 10);
doc.text(lines, margin + indent, currentY);
currentY += (lines.length * 10);
};
// --- PDF Content ---
// Title Block
doc.setFontSize(20);
doc.setFont('Helvetica', 'bold');
doc.setTextColor(46, 139, 87);
doc.text(`SIEM CORRELATION RULE: ${metaData.rule_name.toUpperCase()}`, pageWidth / 2, currentY, { align: 'center' });
currentY += 15;
doc.setFontSize(10);
doc.setFont('Helvetica', 'normal');
doc.setTextColor(100);
addText(`Goal: ${metaData.rule_use_case}`, 10, 'italic', 0);
currentY += 10;
doc.setTextColor(0);
// 1. Metadata Table
addSectionHeader("1. Rule Metadata & Action", [46, 139, 87]);
const metaHead = [["Attribute", "Value"]];
const metaBody = [
["Severity", metaData.rule_severity],
["Action", metaData.rule_action],
["Time Window", `${metaData.time_window_value} ${metaData.time_window_unit}`],
["Aggregate Key", metaData.aggregate_by],
["Tuning Notes", metaData.rule_notes]
];
doc.autoTable({
startY: currentY, head: metaHead, body: metaBody,
theme: 'grid', styles: { fontSize: 10, font: 'Helvetica' },
headStyles: { fillColor: [230, 230, 230], textColor: [0] },
columnStyles: { 0: { fontStyle: 'bold', fillColor: [240, 240, 240] } }
});
currentY = doc.autoTable.previous.finalY + 15;
// 2. Correlation Logic Table
addSectionHeader("2. Event Correlation Logic (Required Events)", [46, 139, 87]);
if (eventConditions.length > 0) {
const logicHead = [
["ID", "Event Name", "Condition", "Min Count", "Description"]
];
const logicBody = eventConditions.map((event, index) => [
`E-${index + 1}`,
event.name,
`${event.field} ${event.operator} ${event.value}`,
event.minCount.toString(),
event.description
]);
doc.autoTable({
startY: currentY,
head: logicHead,
body: logicBody,
theme: 'grid',
headStyles: { fillColor: [74, 78, 105], textColor: [255] }, // Secondary color
styles: { fontSize: 8, cellPadding: 4, font: 'Helvetica' },
columnStyles: {
4: { cellWidth: 150 } // Give more space to description
}
});
currentY = doc.autoTable.previous.finalY + 10;
} else {
addText("No correlation events defined. Rule will not fire.", 11, 'italic', 5);
currentY += 10;
}
doc.save(`${metaData.rule_name.replace(/\s/g, '_')}_SIEM_Rule.pdf`);
}
// --- Tab Navigation ---
const switchTab = (tabIndex) => {
tabs.forEach((tab, index) => {
tab.classList.toggle('active', index === tabIndex);
contents[index].classList.toggle('active', index === tabIndex);
});
currentTab = tabIndex;
updateNavButtons();
if (tabIndex === 2) {
// Generate logic only when switching to review
generateRuleLogic();
}
}
const updateNavButtons = () => {
prevBtn.disabled = currentTab === 0;
nextBtn.disabled = currentTab === tabs.length - 1;
}
tabs.forEach((tab, index) => {
tab.addEventListener('click', () => {
const tabNode = tab.closest('.siem-tab-button');
const newIndex = Array.from(tabNode.parentNode.children).indexOf(tabNode);
switchTab(newIndex);
});
});
nextBtn.addEventListener('click', () => { if (currentTab < tabs.length - 1) switchTab(currentTab + 1); });
prevBtn.addEventListener('click', () => { if (currentTab > 0) switchTab(currentTab - 1); });
// --- Event Listeners and Initial Load ---
generateLogicBtn.addEventListener('click', generateRuleLogic);
pdfDownloadBtn.addEventListener('click', downloadPDF);
// Initial Setup
renderEventConditions();
updateNavButtons();
});