401 lines
16 KiB
Plaintext
401 lines
16 KiB
Plaintext
---
|
|
import { promises as fs } from 'fs';
|
|
import { load } from 'js-yaml';
|
|
import path from 'path';
|
|
|
|
// Load tools data
|
|
const yamlPath = path.join(process.cwd(), 'src/data/tools.yaml');
|
|
const yamlContent = await fs.readFile(yamlPath, 'utf8');
|
|
const data = load(yamlContent) as any;
|
|
|
|
const domains = data.domains;
|
|
const phases = data.phases;
|
|
const tools = data.tools;
|
|
|
|
// Separate collaboration tools from domain-specific tools
|
|
const collaborationTools = tools.filter((tool: any) =>
|
|
tool.phases.includes('collaboration')
|
|
);
|
|
|
|
const domainTools = tools.filter((tool: any) =>
|
|
!tool.phases.includes('collaboration')
|
|
);
|
|
|
|
// Create matrix structure for domain-specific tools only
|
|
const matrix: Record<string, Record<string, any[]>> = {};
|
|
domains.forEach((domain: any) => {
|
|
matrix[domain.id] = {};
|
|
phases.filter((phase: any) => phase.id !== 'collaboration').forEach((phase: any) => {
|
|
matrix[domain.id][phase.id] = domainTools.filter((tool: any) =>
|
|
tool.domains.includes(domain.id) && tool.phases.includes(phase.id)
|
|
);
|
|
});
|
|
});
|
|
---
|
|
|
|
<div id="matrix-container" class="matrix-wrapper" style="display: none;">
|
|
<!-- Collaboration Tools Section (compact horizontal layout for matrix view) -->
|
|
<div id="collaboration-tools-section" style="margin-bottom: 1.5rem;">
|
|
<h3 style="margin-bottom: 0.75rem; color: var(--color-text); font-size: 1.125rem;">Übergreifend & Kollaboration</h3>
|
|
<div class="collaboration-tools-compact" id="collaboration-tools-container">
|
|
{collaborationTools.map((tool: any) => {
|
|
const hasValidProjectUrl = tool.projectUrl !== undefined &&
|
|
tool.projectUrl !== null &&
|
|
tool.projectUrl !== "" &&
|
|
tool.projectUrl.trim() !== "";
|
|
return (
|
|
<div class={`collaboration-tool-compact ${hasValidProjectUrl ? 'hosted' : tool.license !== 'Proprietary' ? 'oss' : ''}`}
|
|
onclick={`window.showToolDetails('${tool.name}')`}>
|
|
<div class="tool-compact-header">
|
|
<h4 style="margin: 0; font-size: 0.875rem; font-weight: 600;">{tool.name}</h4>
|
|
<div style="display: flex; gap: 0.25rem;">
|
|
{hasValidProjectUrl && <span class="badge-mini badge-primary">Self-Hosted</span>}
|
|
{tool.license !== 'Proprietary' && <span class="badge-mini badge-success">OSS</span>}
|
|
</div>
|
|
</div>
|
|
<p style="font-size: 0.75rem; color: var(--color-text-secondary); margin: 0.25rem 0; line-height: 1.3;">
|
|
{tool.description}
|
|
</p>
|
|
<div style="display: flex; gap: 0.75rem; font-size: 0.6875rem; color: var(--color-text-secondary);">
|
|
<span>{tool.platforms.join(', ')}</span>
|
|
<span>•</span>
|
|
<span>{tool.skillLevel}</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- DFIR Tools Matrix -->
|
|
<div id="dfir-matrix-section">
|
|
<h2 style="margin-bottom: 1rem; color: var(--color-text);">DFIR Tools Matrix</h2>
|
|
<table class="matrix-table">
|
|
<thead>
|
|
<tr>
|
|
<th style="width: 200px;">Domain / Phase</th>
|
|
{phases.filter((phase: any) => phase.id !== 'collaboration').map((phase: any) => (
|
|
<th data-phase={phase.id}>{phase.name}</th>
|
|
))}
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{domains.map((domain: any) => (
|
|
<tr data-domain={domain.id}>
|
|
<th>{domain.name}</th>
|
|
{phases.filter((phase: any) => phase.id !== 'collaboration').map((phase: any) => (
|
|
<td class="matrix-cell" data-domain={domain.id} data-phase={phase.id}>
|
|
{matrix[domain.id][phase.id].map((tool: any) => {
|
|
const hasValidProjectUrl = tool.projectUrl !== undefined &&
|
|
tool.projectUrl !== null &&
|
|
tool.projectUrl !== "" &&
|
|
tool.projectUrl.trim() !== "";
|
|
return (
|
|
<span
|
|
class={`tool-chip ${hasValidProjectUrl ? 'tool-chip-hosted' : tool.license !== 'Proprietary' ? 'tool-chip-oss' : ''}`}
|
|
data-tool-name={tool.name}
|
|
onclick={`window.showToolDetails('${tool.name}')`}
|
|
>
|
|
{tool.name}
|
|
</span>
|
|
);
|
|
})}
|
|
</td>
|
|
))}
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tool Details Modal -->
|
|
<div class="modal-overlay" id="modal-overlay" onclick="window.hideToolDetails()"></div>
|
|
<div class="tool-details" id="tool-details">
|
|
<div style="display: flex; justify-content: space-between; align-items: start; margin-bottom: 1rem;">
|
|
<h2 id="tool-name" style="margin: 0;">Tool Name</h2>
|
|
<button class="btn-icon" onclick="window.hideToolDetails()">
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
|
|
<p id="tool-description" class="text-muted"></p>
|
|
|
|
<div id="tool-badges" style="display: flex; gap: 0.5rem; margin-bottom: 1rem;"></div>
|
|
|
|
<div id="tool-metadata" style="margin-bottom: 1rem;"></div>
|
|
|
|
<div id="tool-tags" style="margin-bottom: 1rem;"></div>
|
|
|
|
<!-- Updated button container for dual buttons -->
|
|
<div id="tool-links" style="display: flex; gap: 0.5rem;"></div>
|
|
</div>
|
|
|
|
<script define:vars={{ toolsData: tools, collaborationTools, domainTools }}>
|
|
// Helper function to get selected phase from active button
|
|
function getSelectedPhase() {
|
|
const activePhaseButton = document.querySelector('.phase-button.active');
|
|
return activePhaseButton ? activePhaseButton.getAttribute('data-phase') : '';
|
|
}
|
|
|
|
// Helper function to get selected domain from dropdown
|
|
function getSelectedDomain() {
|
|
const domainSelect = document.getElementById('domain-select');
|
|
return domainSelect ? domainSelect.value : '';
|
|
}
|
|
|
|
// Update matrix highlighting based on current filters
|
|
function updateMatrixHighlighting() {
|
|
const matrixTable = document.querySelector('.matrix-table');
|
|
if (!matrixTable) return;
|
|
|
|
// Clear existing highlights
|
|
matrixTable.querySelectorAll('.highlight-row, .highlight-column').forEach(el => {
|
|
el.classList.remove('highlight-row', 'highlight-column');
|
|
});
|
|
|
|
const selectedDomain = getSelectedDomain();
|
|
const selectedPhase = getSelectedPhase();
|
|
|
|
// Highlight selected domain row
|
|
if (selectedDomain) {
|
|
const domainRow = matrixTable.querySelector(`tr[data-domain="${selectedDomain}"]`);
|
|
if (domainRow) {
|
|
domainRow.classList.add('highlight-row');
|
|
}
|
|
}
|
|
|
|
// Highlight selected phase column
|
|
if (selectedPhase) {
|
|
const phaseHeaders = matrixTable.querySelectorAll('thead th[data-phase]');
|
|
const phaseIndex = Array.from(phaseHeaders).findIndex(th =>
|
|
th.getAttribute('data-phase') === selectedPhase
|
|
);
|
|
|
|
if (phaseIndex >= 0) {
|
|
const columnIndex = phaseIndex + 1; // +1 because first column is domain names
|
|
matrixTable.querySelectorAll(`tr`).forEach(row => {
|
|
const cell = row.children[columnIndex];
|
|
if (cell) {
|
|
cell.classList.add('highlight-column');
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
// Helper function to create compact collaboration tool cards for matrix view
|
|
function createCollaborationToolCardCompact(tool) {
|
|
const hasValidProjectUrl = tool.projectUrl !== undefined &&
|
|
tool.projectUrl !== null &&
|
|
tool.projectUrl !== "" &&
|
|
tool.projectUrl.trim() !== "";
|
|
|
|
const cardDiv = document.createElement('div');
|
|
const cardClass = `collaboration-tool-compact ${hasValidProjectUrl ? 'hosted' : tool.license !== 'Proprietary' ? 'oss' : ''}`;
|
|
cardDiv.className = cardClass;
|
|
cardDiv.onclick = () => window.showToolDetails(tool.name);
|
|
|
|
cardDiv.innerHTML = `
|
|
<div class="tool-compact-header">
|
|
<h4 style="margin: 0; font-size: 0.875rem; font-weight: 600;">${tool.name}</h4>
|
|
<div style="display: flex; gap: 0.25rem;">
|
|
${hasValidProjectUrl ? '<span class="badge-mini badge-primary">Self-Hosted</span>' : ''}
|
|
${tool.license !== 'Proprietary' ? '<span class="badge-mini badge-success">OSS</span>' : ''}
|
|
</div>
|
|
</div>
|
|
<p style="font-size: 0.75rem; color: var(--color-text-secondary); margin: 0.25rem 0; line-height: 1.3;">
|
|
${tool.description}
|
|
</p>
|
|
<div style="display: flex; gap: 0.75rem; font-size: 0.6875rem; color: var(--color-text-secondary);">
|
|
<span>${tool.platforms.join(', ')}</span>
|
|
<span>•</span>
|
|
<span>${tool.skillLevel}</span>
|
|
</div>
|
|
`;
|
|
|
|
return cardDiv;
|
|
}
|
|
|
|
// Tool details functions
|
|
window.showToolDetails = function(toolName) {
|
|
const tool = toolsData.find(t => t.name === toolName);
|
|
if (!tool) return;
|
|
|
|
// Update modal content
|
|
document.getElementById('tool-name').textContent = tool.name;
|
|
document.getElementById('tool-description').textContent = tool.description;
|
|
|
|
// Badges
|
|
const badgesContainer = document.getElementById('tool-badges');
|
|
const hasValidProjectUrl = tool.projectUrl !== undefined &&
|
|
tool.projectUrl !== null &&
|
|
tool.projectUrl !== "" &&
|
|
tool.projectUrl.trim() !== "";
|
|
|
|
badgesContainer.innerHTML = '';
|
|
if (hasValidProjectUrl) {
|
|
badgesContainer.innerHTML += '<span class="badge badge-primary">Self-Hosted</span>';
|
|
}
|
|
if (tool.license !== 'Proprietary') {
|
|
badgesContainer.innerHTML += '<span class="badge badge-success">Open Source</span>';
|
|
}
|
|
|
|
// Metadata
|
|
const metadataContainer = document.getElementById('tool-metadata');
|
|
const domainsText = tool.domains.length > 0 ? tool.domains.join(', ') : 'Domain-agnostic';
|
|
const phasesText = tool.phases.join(', ');
|
|
metadataContainer.innerHTML = `
|
|
<div style="display: grid; gap: 0.5rem;">
|
|
<div><strong>Betriebssystem:</strong> ${tool.platforms.join(', ')}</div>
|
|
<div><strong>Skill Level:</strong> ${tool.skillLevel}</div>
|
|
<div><strong>Lizenzmodell:</strong> ${tool.license}</div>
|
|
<div><strong>Deployment:</strong> ${tool.accessType}</div>
|
|
<div><strong>Einsatzgebiete:</strong> ${domainsText}</div>
|
|
<div><strong>Ermittlungsphasen:</strong> ${phasesText}</div>
|
|
</div>
|
|
`;
|
|
|
|
// Tags
|
|
const tagsContainer = document.getElementById('tool-tags');
|
|
tagsContainer.innerHTML = `
|
|
<div style="display: flex; flex-wrap: wrap; gap: 0.25rem;">
|
|
${tool.tags.map(tag => `<span class="tag">${tag}</span>`).join('')}
|
|
</div>
|
|
`;
|
|
|
|
// Links - Updated to handle dual buttons for hosted tools
|
|
const linksContainer = document.getElementById('tool-links');
|
|
|
|
if (hasValidProjectUrl) {
|
|
// Two buttons for tools we're hosting
|
|
linksContainer.innerHTML = `
|
|
<a href="${tool.url}" target="_blank" rel="noopener noreferrer" class="btn btn-secondary" style="flex: 1;">
|
|
Software-Homepage
|
|
</a>
|
|
<a href="${tool.projectUrl}" target="_blank" rel="noopener noreferrer" class="btn btn-primary" style="flex: 1;">
|
|
Zugreifen
|
|
</a>
|
|
`;
|
|
} else {
|
|
// Single button for tools we're not hosting
|
|
linksContainer.innerHTML = `
|
|
<a href="${tool.url}" target="_blank" rel="noopener noreferrer" class="btn btn-primary" style="width: 100%;">
|
|
Software-Homepage
|
|
</a>
|
|
`;
|
|
}
|
|
|
|
// Show modal
|
|
document.getElementById('modal-overlay').classList.add('active');
|
|
document.getElementById('tool-details').classList.add('active');
|
|
};
|
|
|
|
window.hideToolDetails = function() {
|
|
document.getElementById('modal-overlay').classList.remove('active');
|
|
document.getElementById('tool-details').classList.remove('active');
|
|
};
|
|
|
|
// Listen for view changes to trigger highlighting
|
|
window.addEventListener('viewChanged', (event) => {
|
|
const view = event.detail;
|
|
if (view === 'matrix') {
|
|
// Delay highlighting to ensure matrix is visible
|
|
setTimeout(updateMatrixHighlighting, 100);
|
|
}
|
|
});
|
|
|
|
// Listen for filter changes to update highlighting
|
|
window.addEventListener('toolsFiltered', (event) => {
|
|
const currentView = document.querySelector('.view-toggle.active')?.getAttribute('data-view');
|
|
if (currentView === 'matrix') {
|
|
setTimeout(updateMatrixHighlighting, 50);
|
|
}
|
|
});
|
|
|
|
// Update matrix on filter change
|
|
window.addEventListener('toolsFiltered', (event) => {
|
|
const filtered = event.detail;
|
|
const currentView = document.querySelector('.view-toggle.active')?.getAttribute('data-view');
|
|
|
|
if (currentView === 'matrix') {
|
|
// Get selected phase from active button instead of dropdown
|
|
const selectedPhase = getSelectedPhase();
|
|
|
|
// Handle collaboration tools section
|
|
const collaborationSection = document.getElementById('collaboration-tools-section');
|
|
const dfirMatrixSection = document.getElementById('dfir-matrix-section');
|
|
const collaborationContainer = document.getElementById('collaboration-tools-container');
|
|
|
|
if (selectedPhase === 'collaboration') {
|
|
// Show only collaboration tools, hide matrix
|
|
collaborationSection.style.display = 'block';
|
|
dfirMatrixSection.style.display = 'none';
|
|
|
|
// Filter collaboration tools
|
|
const filteredCollaboration = filtered.filter(tool => tool.phases.includes('collaboration'));
|
|
collaborationContainer.innerHTML = '';
|
|
|
|
filteredCollaboration.forEach(tool => {
|
|
const toolCard = createCollaborationToolCardCompact(tool);
|
|
collaborationContainer.appendChild(toolCard);
|
|
});
|
|
} else {
|
|
// Show matrix, handle collaboration tools visibility
|
|
dfirMatrixSection.style.display = 'block';
|
|
|
|
if (selectedPhase === '' || selectedPhase === null) {
|
|
// Show collaboration tools when no specific phase is selected
|
|
collaborationSection.style.display = 'block';
|
|
|
|
// Show all collaboration tools that pass general filters
|
|
const filteredCollaboration = filtered.filter(tool => tool.phases.includes('collaboration'));
|
|
collaborationContainer.innerHTML = '';
|
|
|
|
filteredCollaboration.forEach(tool => {
|
|
const toolCard = createCollaborationToolCardCompact(tool);
|
|
collaborationContainer.appendChild(toolCard);
|
|
});
|
|
} else {
|
|
// Hide collaboration tools when specific DFIR phase is selected
|
|
collaborationSection.style.display = 'none';
|
|
}
|
|
|
|
// Clear and update matrix cells with DFIR tools only
|
|
document.querySelectorAll('.matrix-cell').forEach(cell => {
|
|
cell.innerHTML = '';
|
|
});
|
|
|
|
// Re-populate with filtered DFIR tools
|
|
const filteredDfirTools = filtered.filter(tool => !tool.phases.includes('collaboration'));
|
|
filteredDfirTools.forEach(tool => {
|
|
const hasValidProjectUrl = tool.projectUrl !== undefined &&
|
|
tool.projectUrl !== null &&
|
|
tool.projectUrl !== "" &&
|
|
tool.projectUrl.trim() !== "";
|
|
|
|
tool.domains.forEach(domain => {
|
|
tool.phases.forEach(phase => {
|
|
if (phase !== 'collaboration') {
|
|
const cell = document.querySelector(`[data-domain="${domain}"][data-phase="${phase}"]`);
|
|
if (cell) {
|
|
const chip = document.createElement('span');
|
|
chip.className = `tool-chip ${hasValidProjectUrl ? 'tool-chip-hosted' : tool.license !== 'Proprietary' ? 'tool-chip-oss' : ''}`;
|
|
chip.textContent = tool.name;
|
|
chip.onclick = () => window.showToolDetails(tool.name);
|
|
cell.appendChild(chip);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
// Update highlighting after matrix content is updated
|
|
setTimeout(updateMatrixHighlighting, 50);
|
|
}
|
|
}
|
|
});
|
|
</script> |