change tools->meToCo

This commit is contained in:
overcuriousity 2025-08-04 21:52:10 +02:00
parent f00e2d3cfd
commit 223d28f094
27 changed files with 366 additions and 366 deletions

View File

@ -315,7 +315,7 @@ PUBLIC_BASE_URL=http://localhost:4321
Die Tools werden in `src/data/tools.yaml` verwaltet. Vollständiges Beispiel:
```yaml
tools:
meToCo:
- name: Autopsy
type: software # software|method|concept
description: >-
@ -431,7 +431,7 @@ phases:
domain-agnostic-software:
- id: collaboration-general
name: Übergreifend & Kollaboration
description: Cross-cutting tools and collaboration platforms
description: Cross-cutting meToCo and collaboration platforms
- id: specific-os
name: Betriebssysteme
description: Operating Systems which focus on forensics

View File

@ -158,7 +158,7 @@
text-align: center;
}
.tools-grid {
.meToCo-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
@ -370,7 +370,7 @@
.stats-grid {
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.tools-grid {
.meToCo-grid {
grid-template-columns: 1fr;
}
.checkbox-group {
@ -396,12 +396,12 @@
<div class="container">
<div class="header">
<h1>🔧 DFIR Tools YAML Editor</h1>
<p>Enhanced editor for Digital Forensics and Incident Response tools, methods, and concepts</p>
<p>Enhanced editor for Digital Forensics and Incident Response meToCo, methods, and concepts</p>
</div>
<div class="tabs">
<div class="tab active" onclick="showTab('overview')">📊 Overview</div>
<div class="tab" onclick="showTab('tools')">🛠️ Tools & Concepts</div>
<div class="tab" onclick="showTab('meToCo')">🛠️ Tools & Concepts</div>
<div class="tab" onclick="showTab('editor')">✏️ Editor</div>
<div class="tab" onclick="showTab('bulk')">📋 Bulk Edit</div>
<div class="tab" onclick="showTab('knowledge')">📚 Knowledge Generator</div>
@ -413,7 +413,7 @@
<div style="background: var(--light); padding: 20px; border-radius: 10px; margin-bottom: 20px; border: 2px dashed var(--border); text-align: center;">
<h3>📁 Load YAML File</h3>
<input type="file" id="fileInput" accept=".yaml,.yml" onchange="loadFile()">
<p>Select a YAML file to load existing tools data</p>
<p>Select a YAML file to load existing meToCo data</p>
</div>
<div id="stats" class="stats-grid"></div>
@ -430,7 +430,7 @@
</div>
<!-- Tools Tab with Enhanced Search -->
<div id="tools" class="tab-content">
<div id="meToCo" class="tab-content">
<div class="search-container">
<h3>🔍 Search & Filter Tools</h3>
<input type="text"
@ -460,7 +460,7 @@
<button class="btn btn-secondary" onclick="clearFilters()">Clear All</button>
</div>
</div>
<div id="toolsGrid" class="tools-grid"></div>
<div id="meToCoGrid" class="meToCo-grid"></div>
</div>
<!-- Enhanced Editor Tab -->
@ -645,7 +645,7 @@
<div class="bulk-section">
<h3>📋 Bulk Operations</h3>
<div id="selectionInfo" style="margin-bottom: 15px; padding: 15px; background: white; border-radius: 8px; font-weight: 600; text-align: center; border: 2px solid var(--primary);">
No tools selected
No meToCo selected
</div>
<div class="bulk-controls">
@ -702,7 +702,7 @@
<button class="btn btn-danger" onclick="bulkDelete()">💀 Delete Selected Tools</button>
</div>
</div>
<div id="bulkToolsGrid" class="tools-grid"></div>
<div id="bulkToolsGrid" class="meToCo-grid"></div>
</div>
<!-- Knowledge Generator Tab -->
@ -755,7 +755,7 @@
// Initialize with correct YAML structure including scenarios
function init() {
yamlData = {
tools: [],
meToCo: [],
domains: [
{ id: 'incident-response', name: 'Incident Response & Breach-Untersuchung' },
{ id: 'static-investigations', name: 'Datenträgerforensik & Ermittlungen' },
@ -773,7 +773,7 @@
{ id: 'reporting', name: 'Bericht & Präsentation', description: 'Documentation, Visualization, Presentation Tools' }
],
'domain-agnostic-software': [
{ id: 'collaboration-general', name: 'Übergreifend & Kollaboration', description: 'Cross-cutting tools and collaboration platforms' },
{ id: 'collaboration-general', name: 'Übergreifend & Kollaboration', description: 'Cross-cutting meToCo and collaboration platforms' },
{ id: 'specific-os', name: 'Betriebssysteme', description: 'Operating Systems which focus on forensics' }
],
scenarios: [
@ -801,7 +801,7 @@
document.getElementById(tabName).classList.add('active');
event.target.classList.add('active');
if (tabName === 'tools') renderToolsGrid();
if (tabName === 'meToCo') renderToolsGrid();
else if (tabName === 'bulk') renderBulkGrid();
else if (tabName === 'knowledge') updateKnowledgeToolSelect();
}
@ -811,11 +811,11 @@
applyFilters();
}
function searchTools(tools, searchTerm) {
if (!searchTerm) return tools;
function searchTools(meToCo, searchTerm) {
if (!searchTerm) return meToCo;
const term = searchTerm.toLowerCase();
return tools.filter(tool => {
return meToCo.filter(tool => {
// Search in name
if (tool.name && tool.name.toLowerCase().includes(term)) return true;
@ -946,15 +946,15 @@
}
function updateStats() {
if (!yamlData?.tools) return;
if (!yamlData?.meToCo) return;
const stats = {
total: yamlData.tools.length,
software: yamlData.tools.filter(t => t.type === 'software' || !t.type).length,
methods: yamlData.tools.filter(t => t.type === 'method').length,
concepts: yamlData.tools.filter(t => t.type === 'concept').length,
withKnowledgebase: yamlData.tools.filter(t => t.knowledgebase).length,
withRelatedSoftware: yamlData.tools.filter(t => t.related_software && t.related_software.length > 0).length
total: yamlData.meToCo.length,
software: yamlData.meToCo.filter(t => t.type === 'software' || !t.type).length,
methods: yamlData.meToCo.filter(t => t.type === 'method').length,
concepts: yamlData.meToCo.filter(t => t.type === 'concept').length,
withKnowledgebase: yamlData.meToCo.filter(t => t.knowledgebase).length,
withRelatedSoftware: yamlData.meToCo.filter(t => t.related_software && t.related_software.length > 0).length
};
document.getElementById('stats').innerHTML = `
@ -1042,8 +1042,8 @@
function saveTool() {
try {
if (!yamlData) yamlData = { tools: [] };
if (!yamlData.tools) yamlData.tools = [];
if (!yamlData) yamlData = { meToCo: [] };
if (!yamlData.meToCo) yamlData.meToCo = [];
const toolType = document.getElementById('toolType').value;
const tool = {
@ -1104,11 +1104,11 @@
}
if (currentEditingIndex >= 0) {
yamlData.tools[currentEditingIndex] = tool;
yamlData.meToCo[currentEditingIndex] = tool;
showMessage('Tool updated successfully!');
currentEditingIndex = -1;
} else {
yamlData.tools.push(tool);
yamlData.meToCo.push(tool);
showMessage('Tool added successfully!');
}
@ -1146,7 +1146,7 @@
}
function editTool(index) {
const tool = yamlData.tools[index];
const tool = yamlData.meToCo[index];
currentEditingIndex = index;
// Populate form fields
@ -1208,7 +1208,7 @@
function deleteTool(index) {
if (confirm('Are you sure you want to delete this tool?')) {
yamlData.tools.splice(index, 1);
yamlData.meToCo.splice(index, 1);
showMessage('Tool deleted successfully!');
updateStats();
renderToolsGrid();
@ -1217,12 +1217,12 @@
}
function renderToolsGrid() {
const container = document.getElementById('toolsGrid');
const container = document.getElementById('meToCoGrid');
container.innerHTML = '';
if (!yamlData?.tools) return;
if (!yamlData?.meToCo) return;
let filteredTools = yamlData.tools;
let filteredTools = yamlData.meToCo;
// Apply search filter
const searchTerm = document.getElementById('searchInput')?.value;
@ -1243,17 +1243,17 @@
// Update search results info
const searchResults = document.getElementById('searchResults');
if (searchResults) {
const totalTools = yamlData.tools.length;
const totalTools = yamlData.meToCo.length;
const filteredCount = filteredTools.length;
if (searchTerm || typeFilter || skillFilter) {
searchResults.textContent = `Showing ${filteredCount} of ${totalTools} tools`;
searchResults.textContent = `Showing ${filteredCount} of ${totalTools} meToCo`;
} else {
searchResults.textContent = `Showing all ${totalTools} tools`;
searchResults.textContent = `Showing all ${totalTools} meToCo`;
}
}
filteredTools.forEach((tool, index) => {
const originalIndex = yamlData.tools.indexOf(tool);
const originalIndex = yamlData.meToCo.indexOf(tool);
const card = createToolCard(tool, originalIndex);
container.appendChild(card);
});
@ -1296,9 +1296,9 @@
const container = document.getElementById('bulkToolsGrid');
container.innerHTML = '';
if (!yamlData?.tools) return;
if (!yamlData?.meToCo) return;
yamlData.tools.forEach((tool, index) => {
yamlData.meToCo.forEach((tool, index) => {
const card = createBulkToolCard(tool, index);
container.appendChild(card);
});
@ -1350,7 +1350,7 @@
function selectAllTools() {
selectedTools.clear();
yamlData.tools.forEach((_, index) => selectedTools.add(index));
yamlData.meToCo.forEach((_, index) => selectedTools.add(index));
updateSelectionCount();
renderBulkGrid();
}
@ -1365,7 +1365,7 @@
const count = selectedTools.size;
const info = document.getElementById('selectionInfo');
if (count === 0) {
info.textContent = 'No tools selected';
info.textContent = 'No meToCo selected';
info.style.background = 'white';
info.style.borderColor = 'var(--border)';
} else {
@ -1377,175 +1377,175 @@
// Enhanced bulk operations with scenarios and related software
function bulkSetType() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
const newType = prompt('Enter new type (software/method/concept):');
if (newType && ['software', 'method', 'concept'].includes(newType)) {
selectedTools.forEach(index => yamlData.tools[index].type = newType);
showMessage(`Updated type for ${selectedTools.size} tools`);
selectedTools.forEach(index => yamlData.meToCo[index].type = newType);
showMessage(`Updated type for ${selectedTools.size} meToCo`);
updateStats();
renderBulkGrid();
}
}
function bulkSetSkillLevel() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
const newLevel = prompt('Enter skill level (novice/beginner/intermediate/advanced/expert):');
if (newLevel && ['novice', 'beginner', 'intermediate', 'advanced', 'expert'].includes(newLevel)) {
selectedTools.forEach(index => yamlData.tools[index].skillLevel = newLevel);
showMessage(`Updated skill level for ${selectedTools.size} tools`);
selectedTools.forEach(index => yamlData.meToCo[index].skillLevel = newLevel);
showMessage(`Updated skill level for ${selectedTools.size} meToCo`);
renderBulkGrid();
}
}
function bulkAddTags() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
const tags = prompt('Enter tags to add (comma-separated):');
if (tags) {
const tagList = tags.split(',').map(t => t.trim()).filter(t => t);
selectedTools.forEach(index => {
const tool = yamlData.tools[index];
const tool = yamlData.meToCo[index];
tool.tags = [...new Set([...(tool.tags || []), ...tagList])];
});
showMessage(`Added tags to ${selectedTools.size} tools`);
showMessage(`Added tags to ${selectedTools.size} meToCo`);
renderBulkGrid();
}
}
function bulkRemoveTags() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
const tags = prompt('Enter tags to remove (comma-separated):');
if (tags) {
const tagList = tags.split(',').map(t => t.trim()).filter(t => t);
selectedTools.forEach(index => {
const tool = yamlData.tools[index];
const tool = yamlData.meToCo[index];
if (tool.tags) {
tool.tags = tool.tags.filter(tag => !tagList.includes(tag));
if (tool.tags.length === 0) delete tool.tags;
}
});
showMessage(`Removed tags from ${selectedTools.size} tools`);
showMessage(`Removed tags from ${selectedTools.size} meToCo`);
renderBulkGrid();
}
}
function bulkReplaceTags() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
const tags = prompt('Enter new tags (comma-separated, will replace all existing tags):');
if (tags !== null) {
const tagList = tags.split(',').map(t => t.trim()).filter(t => t);
selectedTools.forEach(index => {
const tool = yamlData.tools[index];
const tool = yamlData.meToCo[index];
if (tagList.length > 0) {
tool.tags = tagList;
} else {
delete tool.tags;
}
});
showMessage(`Replaced tags for ${selectedTools.size} tools`);
showMessage(`Replaced tags for ${selectedTools.size} meToCo`);
renderBulkGrid();
}
}
function bulkClearTags() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (confirm(`Are you sure you want to clear ALL tags from ${selectedTools.size} selected tools?`)) {
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
if (confirm(`Are you sure you want to clear ALL tags from ${selectedTools.size} selected meToCo?`)) {
selectedTools.forEach(index => {
delete yamlData.tools[index].tags;
delete yamlData.meToCo[index].tags;
});
showMessage(`Cleared tags from ${selectedTools.size} tools`);
showMessage(`Cleared tags from ${selectedTools.size} meToCo`);
renderBulkGrid();
}
}
// Domain operations
function bulkAddDomains() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
const domains = prompt('Enter domain IDs to add (comma-separated):');
if (domains) {
const domainList = domains.split(',').map(d => d.trim()).filter(d => d);
selectedTools.forEach(index => {
const tool = yamlData.tools[index];
const tool = yamlData.meToCo[index];
tool.domains = [...new Set([...(tool.domains || []), ...domainList])];
});
showMessage(`Added domains to ${selectedTools.size} tools`);
showMessage(`Added domains to ${selectedTools.size} meToCo`);
renderBulkGrid();
}
}
function bulkRemoveDomains() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
const domains = prompt('Enter domain IDs to remove (comma-separated):');
if (domains) {
const domainList = domains.split(',').map(d => d.trim()).filter(d => d);
selectedTools.forEach(index => {
const tool = yamlData.tools[index];
const tool = yamlData.meToCo[index];
if (tool.domains) {
tool.domains = tool.domains.filter(domain => !domainList.includes(domain));
if (tool.domains.length === 0) delete tool.domains;
}
});
showMessage(`Removed domains from ${selectedTools.size} tools`);
showMessage(`Removed domains from ${selectedTools.size} meToCo`);
renderBulkGrid();
}
}
function bulkClearDomains() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (confirm(`Are you sure you want to clear ALL domains from ${selectedTools.size} selected tools?`)) {
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
if (confirm(`Are you sure you want to clear ALL domains from ${selectedTools.size} selected meToCo?`)) {
selectedTools.forEach(index => {
delete yamlData.tools[index].domains;
delete yamlData.meToCo[index].domains;
});
showMessage(`Cleared domains from ${selectedTools.size} tools`);
showMessage(`Cleared domains from ${selectedTools.size} meToCo`);
renderBulkGrid();
}
}
// Phase operations
function bulkAddPhases() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
const phases = prompt('Enter phase IDs to add (comma-separated):');
if (phases) {
const phaseList = phases.split(',').map(p => p.trim()).filter(p => p);
selectedTools.forEach(index => {
const tool = yamlData.tools[index];
const tool = yamlData.meToCo[index];
tool.phases = [...new Set([...(tool.phases || []), ...phaseList])];
});
showMessage(`Added phases to ${selectedTools.size} tools`);
showMessage(`Added phases to ${selectedTools.size} meToCo`);
renderBulkGrid();
}
}
function bulkRemovePhases() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
const phases = prompt('Enter phase IDs to remove (comma-separated):');
if (phases) {
const phaseList = phases.split(',').map(p => p.trim()).filter(p => p);
selectedTools.forEach(index => {
const tool = yamlData.tools[index];
const tool = yamlData.meToCo[index];
if (tool.phases) {
tool.phases = tool.phases.filter(phase => !phaseList.includes(phase));
if (tool.phases.length === 0) delete tool.phases;
}
});
showMessage(`Removed phases from ${selectedTools.size} tools`);
showMessage(`Removed phases from ${selectedTools.size} meToCo`);
renderBulkGrid();
}
}
function bulkClearPhases() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (confirm(`Are you sure you want to clear ALL phases from ${selectedTools.size} selected tools?`)) {
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
if (confirm(`Are you sure you want to clear ALL phases from ${selectedTools.size} selected meToCo?`)) {
selectedTools.forEach(index => {
delete yamlData.tools[index].phases;
delete yamlData.meToCo[index].phases;
});
showMessage(`Cleared phases from ${selectedTools.size} tools`);
showMessage(`Cleared phases from ${selectedTools.size} meToCo`);
renderBulkGrid();
}
}
// Scenario operations (work with tags that have scenario: prefix)
function bulkAddScenarios() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
const scenarios = prompt('Enter scenario IDs to add (comma-separated, e.g., scenario:memory_dump,scenario:registry):');
if (scenarios) {
const scenarioList = scenarios.split(',').map(s => {
@ -1553,16 +1553,16 @@
return trimmed.startsWith('scenario:') ? trimmed : `scenario:${trimmed}`;
}).filter(s => s !== 'scenario:');
selectedTools.forEach(index => {
const tool = yamlData.tools[index];
const tool = yamlData.meToCo[index];
tool.tags = [...new Set([...(tool.tags || []), ...scenarioList])];
});
showMessage(`Added scenario tags to ${selectedTools.size} tools`);
showMessage(`Added scenario tags to ${selectedTools.size} meToCo`);
renderBulkGrid();
}
}
function bulkRemoveScenarios() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
const scenarios = prompt('Enter scenario IDs to remove (comma-separated):');
if (scenarios) {
const scenarioList = scenarios.split(',').map(s => {
@ -1570,168 +1570,168 @@
return trimmed.startsWith('scenario:') ? trimmed : `scenario:${trimmed}`;
}).filter(s => s !== 'scenario:');
selectedTools.forEach(index => {
const tool = yamlData.tools[index];
const tool = yamlData.meToCo[index];
if (tool.tags) {
tool.tags = tool.tags.filter(tag => !scenarioList.includes(tag));
if (tool.tags.length === 0) delete tool.tags;
}
});
showMessage(`Removed scenario tags from ${selectedTools.size} tools`);
showMessage(`Removed scenario tags from ${selectedTools.size} meToCo`);
renderBulkGrid();
}
}
function bulkClearScenarios() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (confirm(`Are you sure you want to clear ALL scenario tags from ${selectedTools.size} selected tools?`)) {
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
if (confirm(`Are you sure you want to clear ALL scenario tags from ${selectedTools.size} selected meToCo?`)) {
selectedTools.forEach(index => {
const tool = yamlData.tools[index];
const tool = yamlData.meToCo[index];
if (tool.tags) {
tool.tags = tool.tags.filter(tag => !tag.startsWith('scenario:'));
if (tool.tags.length === 0) delete tool.tags;
}
});
showMessage(`Cleared scenario tags from ${selectedTools.size} tools`);
showMessage(`Cleared scenario tags from ${selectedTools.size} meToCo`);
renderBulkGrid();
}
}
// Platform operations
function bulkAddPlatforms() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
const platforms = prompt('Enter platforms to add (comma-separated, e.g., Windows,Linux,macOS):');
if (platforms) {
const platformList = platforms.split(',').map(p => p.trim()).filter(p => p);
selectedTools.forEach(index => {
const tool = yamlData.tools[index];
const tool = yamlData.meToCo[index];
tool.platforms = [...new Set([...(tool.platforms || []), ...platformList])];
});
showMessage(`Added platforms to ${selectedTools.size} tools`);
showMessage(`Added platforms to ${selectedTools.size} meToCo`);
renderBulkGrid();
}
}
function bulkRemovePlatforms() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
const platforms = prompt('Enter platforms to remove (comma-separated):');
if (platforms) {
const platformList = platforms.split(',').map(p => p.trim()).filter(p => p);
selectedTools.forEach(index => {
const tool = yamlData.tools[index];
const tool = yamlData.meToCo[index];
if (tool.platforms) {
tool.platforms = tool.platforms.filter(platform => !platformList.includes(platform));
if (tool.platforms.length === 0) delete tool.platforms;
}
});
showMessage(`Removed platforms from ${selectedTools.size} tools`);
showMessage(`Removed platforms from ${selectedTools.size} meToCo`);
renderBulkGrid();
}
}
function bulkClearPlatforms() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (confirm(`Are you sure you want to clear ALL platforms from ${selectedTools.size} selected tools?`)) {
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
if (confirm(`Are you sure you want to clear ALL platforms from ${selectedTools.size} selected meToCo?`)) {
selectedTools.forEach(index => {
delete yamlData.tools[index].platforms;
delete yamlData.meToCo[index].platforms;
});
showMessage(`Cleared platforms from ${selectedTools.size} tools`);
showMessage(`Cleared platforms from ${selectedTools.size} meToCo`);
renderBulkGrid();
}
}
// Related concepts operations
function bulkAddRelatedConcepts() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
const concepts = prompt('Enter related concept names to add (comma-separated):');
if (concepts) {
const conceptList = concepts.split(',').map(c => c.trim()).filter(c => c);
selectedTools.forEach(index => {
const tool = yamlData.tools[index];
const tool = yamlData.meToCo[index];
tool.related_concepts = [...new Set([...(tool.related_concepts || []), ...conceptList])];
});
showMessage(`Added related concepts to ${selectedTools.size} tools`);
showMessage(`Added related concepts to ${selectedTools.size} meToCo`);
renderBulkGrid();
}
}
function bulkRemoveRelatedConcepts() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
const concepts = prompt('Enter related concept names to remove (comma-separated):');
if (concepts) {
const conceptList = concepts.split(',').map(c => c.trim()).filter(c => c);
selectedTools.forEach(index => {
const tool = yamlData.tools[index];
const tool = yamlData.meToCo[index];
if (tool.related_concepts) {
tool.related_concepts = tool.related_concepts.filter(concept => !conceptList.includes(concept));
if (tool.related_concepts.length === 0) delete tool.related_concepts;
}
});
showMessage(`Removed related concepts from ${selectedTools.size} tools`);
showMessage(`Removed related concepts from ${selectedTools.size} meToCo`);
renderBulkGrid();
}
}
function bulkClearRelatedConcepts() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (confirm(`Are you sure you want to clear ALL related concepts from ${selectedTools.size} selected tools?`)) {
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
if (confirm(`Are you sure you want to clear ALL related concepts from ${selectedTools.size} selected meToCo?`)) {
selectedTools.forEach(index => {
delete yamlData.tools[index].related_concepts;
delete yamlData.meToCo[index].related_concepts;
});
showMessage(`Cleared related concepts from ${selectedTools.size} tools`);
showMessage(`Cleared related concepts from ${selectedTools.size} meToCo`);
renderBulkGrid();
}
}
// NEW: Related software operations
function bulkAddRelatedSoftware() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
const software = prompt('Enter related software names to add (comma-separated):');
if (software) {
const softwareList = software.split(',').map(s => s.trim()).filter(s => s);
selectedTools.forEach(index => {
const tool = yamlData.tools[index];
const tool = yamlData.meToCo[index];
tool.related_software = [...new Set([...(tool.related_software || []), ...softwareList])];
});
showMessage(`Added related software to ${selectedTools.size} tools`);
showMessage(`Added related software to ${selectedTools.size} meToCo`);
renderBulkGrid();
}
}
function bulkRemoveRelatedSoftware() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
const software = prompt('Enter related software names to remove (comma-separated):');
if (software) {
const softwareList = software.split(',').map(s => s.trim()).filter(s => s);
selectedTools.forEach(index => {
const tool = yamlData.tools[index];
const tool = yamlData.meToCo[index];
if (tool.related_software) {
tool.related_software = tool.related_software.filter(sw => !softwareList.includes(sw));
if (tool.related_software.length === 0) delete tool.related_software;
}
});
showMessage(`Removed related software from ${selectedTools.size} tools`);
showMessage(`Removed related software from ${selectedTools.size} meToCo`);
renderBulkGrid();
}
}
function bulkClearRelatedSoftware() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (confirm(`Are you sure you want to clear ALL related software from ${selectedTools.size} selected tools?`)) {
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
if (confirm(`Are you sure you want to clear ALL related software from ${selectedTools.size} selected meToCo?`)) {
selectedTools.forEach(index => {
delete yamlData.tools[index].related_software;
delete yamlData.meToCo[index].related_software;
});
showMessage(`Cleared related software from ${selectedTools.size} tools`);
showMessage(`Cleared related software from ${selectedTools.size} meToCo`);
renderBulkGrid();
}
}
function bulkDelete() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (confirm(`Are you sure you want to delete ${selectedTools.size} selected tools? This action cannot be undone!`)) {
if (selectedTools.size === 0) return showMessage('No meToCo selected', 'error');
if (confirm(`Are you sure you want to delete ${selectedTools.size} selected meToCo? This action cannot be undone!`)) {
const indicesToDelete = Array.from(selectedTools).sort((a, b) => b - a);
indicesToDelete.forEach(index => yamlData.tools.splice(index, 1));
indicesToDelete.forEach(index => yamlData.meToCo.splice(index, 1));
selectedTools.clear();
showMessage(`Deleted ${indicesToDelete.length} tools successfully!`);
showMessage(`Deleted ${indicesToDelete.length} meToCo successfully!`);
updateStats();
renderBulkGrid();
updateKnowledgeToolSelect();
@ -1743,9 +1743,9 @@
const select = document.getElementById('knowledgeToolSelect');
select.innerHTML = '<option value="">Choose a tool or concept...</option>';
if (!yamlData?.tools) return;
if (!yamlData?.meToCo) return;
yamlData.tools.forEach((tool, index) => {
yamlData.meToCo.forEach((tool, index) => {
const option = document.createElement('option');
option.value = index;
option.textContent = `${tool.icon ? tool.icon + ' ' : ''}${tool.name} (${tool.type || 'software'})`;
@ -1762,7 +1762,7 @@
return;
}
const tool = yamlData.tools[index];
const tool = yamlData.meToCo[index];
const template = createCC24MarkdownTemplate(tool);
document.getElementById('markdownContent').value = template;
@ -1781,10 +1781,10 @@ title: "${tool.name}"
description: "${tool.description.split('\n')[0].trim()}"
last_updated: ${new Date().toISOString().split('T')[0]}
tool_name: "${tool.name}"
related_tools: ${JSON.stringify([...(tool.related_concepts || []), ...(tool.related_software || [])])}
related_meToCo: ${JSON.stringify([...(tool.related_concepts || []), ...(tool.related_software || [])])}
author: "CC24-Team"
difficulty: "${tool.skillLevel || 'intermediate'}"
categories: ${tool.type === 'concept' ? '["concepts"]' : tool.type === 'method' ? '["methods"]' : '["tools"]'}
categories: ${tool.type === 'concept' ? '["concepts"]' : tool.type === 'method' ? '["methods"]' : '["meToCo"]'}
tags: ${tool.tags ? JSON.stringify(tool.tags) : '[]'}
published: true
---
@ -1871,7 +1871,7 @@ TODO: Füge weitere nützliche Links und Ressourcen hinzu.
const content = document.getElementById('markdownContent').value;
const select = document.getElementById('knowledgeToolSelect');
const index = parseInt(select.value);
const tool = yamlData.tools[index];
const tool = yamlData.meToCo[index];
const toolSlug = tool.name.toLowerCase()
.replace(/[^a-z0-9\s-]/g, '')
@ -1909,13 +1909,13 @@ TODO: Füge weitere nützliche Links und Ressourcen hinzu.
const validationResults = [];
// Check required sections
if (!yamlData.tools) validationResults.push('❌ Missing tools section');
if (!yamlData.meToCo) validationResults.push('❌ Missing meToCo section');
if (!yamlData.domains) validationResults.push('❌ Missing domains section');
if (!yamlData.phases) validationResults.push('❌ Missing phases section');
if (!yamlData.scenarios) validationResults.push('⚠️ Missing scenarios section (for reference)');
// Validate tools
yamlData.tools?.forEach((tool, index) => {
// Validate meToCo
yamlData.meToCo?.forEach((tool, index) => {
if (!tool.name) validationResults.push(`❌ Tool ${index + 1}: Missing name`);
if (!tool.description) validationResults.push(`❌ Tool ${index + 1}: Missing description`);
if (!tool.skillLevel) validationResults.push(`❌ Tool ${index + 1}: Missing skillLevel`);
@ -1936,9 +1936,9 @@ TODO: Füge weitere nützliche Links und Ressourcen hinzu.
// Validate related_software references
if (tool.related_software && tool.related_software.length > 0) {
tool.related_software.forEach(relatedName => {
const exists = yamlData.tools.some(t => t.name === relatedName);
const exists = yamlData.meToCo.some(t => t.name === relatedName);
if (!exists) {
validationResults.push(`⚠️ Tool ${index + 1}: Related software "${relatedName}" not found in tools`);
validationResults.push(`⚠️ Tool ${index + 1}: Related software "${relatedName}" not found in meToCo`);
}
});
}
@ -2000,7 +2000,7 @@ TODO: Füge weitere nützliche Links und Ressourcen hinzu.
const a = document.createElement('a');
a.href = url;
a.download = 'tools.json';
a.download = 'meToCo.json';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);

View File

@ -1,5 +1,5 @@
{
"name": "dfir-tools-hub",
"name": "forensicpathways",
"type": "module",
"version": "1.0.0",
"scripts": {

View File

@ -2,7 +2,7 @@
import { getToolsData } from '../utils/dataService.js';
const data = await getToolsData();
const tools = data.tools;
const meToCo = data.meToCo;
const phases = data.phases;
const domainAgnosticSoftware = data['domain-agnostic-software'] || [];
---
@ -176,7 +176,7 @@ const domainAgnosticSoftware = data['domain-agnostic-software'] || [];
<div class="micro-step" data-step="scenario">📋 Problemanalyse</div>
<div class="micro-step" data-step="approach">🎯 Ermittlungsansatz</div>
<div class="micro-step" data-step="considerations">⚠️ Herausforderungen</div>
<div class="micro-step" data-step="tools">🔧 Methoden</div>
<div class="micro-step" data-step="meToCo">🔧 Methoden</div>
<div class="micro-step" data-step="knowledge">📚 Evaluation</div>
<div class="micro-step" data-step="final">✅ Audit-Trail</div>
</div>
@ -210,7 +210,7 @@ const domainAgnosticSoftware = data['domain-agnostic-software'] || [];
</div>
</section>
<script define:vars={{ tools, phases, domainAgnosticSoftware }}>
<script define:vars={{ meToCo, phases, domainAgnosticSoftware }}>
class AIQueryInterface {
constructor() {
@ -626,7 +626,7 @@ class AIQueryInterface {
this.showElement(this.elements.microTaskProgress);
this.currentMicroTaskStep = 0;
const steps = ['scenario', 'approach', 'considerations', 'tools', 'knowledge', 'final'];
const steps = ['scenario', 'approach', 'considerations', 'meToCo', 'knowledge', 'final'];
const stepElements = this.elements.microTaskProgress.querySelectorAll('.micro-step');
stepElements.forEach(step => {
@ -681,7 +681,7 @@ class AIQueryInterface {
}
displayWorkflowResults(recommendation, originalQuery) {
const toolsByPhase = {};
const meToCoByPhase = {};
const phaseOrder = phases.map(phase => phase.id);
const phaseNames = phases.reduce((acc, phase) => {
acc[phase.id] = phase.name;
@ -689,14 +689,14 @@ class AIQueryInterface {
}, {});
phaseOrder.forEach(phase => {
toolsByPhase[phase] = [];
meToCoByPhase[phase] = [];
});
recommendation.recommended_tools?.forEach(recTool => {
if (toolsByPhase[recTool.phase]) {
const fullTool = tools.find(t => t.name === recTool.name);
recommendation.recommended_meToCo?.forEach(recTool => {
if (meToCoByPhase[recTool.phase]) {
const fullTool = meToCo.find(t => t.name === recTool.name);
if (fullTool) {
toolsByPhase[recTool.phase].push({
meToCoByPhase[recTool.phase].push({
...fullTool,
recommendation: recTool
});
@ -709,7 +709,7 @@ class AIQueryInterface {
${this.renderHeader('Untersuchungsansatz', originalQuery)}
${this.renderContextualAnalysis(recommendation, 'workflow')}
${this.renderBackgroundKnowledge(recommendation.background_knowledge)}
${this.renderWorkflowPhases(toolsByPhase, phaseOrder, phaseNames)}
${this.renderWorkflowPhases(meToCoByPhase, phaseOrder, phaseNames)}
${this.renderWorkflowSuggestion(recommendation.workflow_suggestion)}
${this.renderAuditTrail(recommendation.auditTrail)}
</div>
@ -724,7 +724,7 @@ class AIQueryInterface {
${this.renderHeader('Handlungsempfehlung', originalQuery)}
${this.renderContextualAnalysis(recommendation, 'tool')}
${this.renderBackgroundKnowledge(recommendation.background_knowledge)}
${this.renderToolRecommendations(recommendation.recommended_tools)}
${this.renderToolRecommendations(recommendation.recommended_meToCo)}
${this.renderAdditionalConsiderations(recommendation.additional_considerations)}
${this.renderAuditTrail(recommendation.auditTrail)}
</div>
@ -1076,9 +1076,9 @@ class AIQueryInterface {
`;
}
renderWorkflowPhases(toolsByPhase, phaseOrder, phaseNames) {
renderWorkflowPhases(meToCoByPhase, phaseOrder, phaseNames) {
return phaseOrder.map((phase, index) => {
const phaseTools = toolsByPhase[phase];
const phaseTools = meToCoByPhase[phase];
if (phaseTools.length === 0) return '';
return `
@ -1087,7 +1087,7 @@ class AIQueryInterface {
<div class="phase-number">${index + 1}</div>
<div class="phase-info">
<h3 class="phase-title">${phaseNames[phase]}</h3>
<div class="phase-tools">
<div class="phase-meToCo">
${phaseTools.map(tool => this.renderWorkflowTool(tool)).join('')}
</div>
</div>
@ -1139,7 +1139,7 @@ class AIQueryInterface {
return `
<div class="tool-recommendations-grid" style="display: grid; gap: 1.5rem;">
${recommendedTools.map((toolRec, index) => {
const fullTool = tools.find(t => t.name === toolRec.name);
const fullTool = meToCo.find(t => t.name === toolRec.name);
if (!fullTool) return '';
return this.renderDetailedTool(fullTool, toolRec, index + 1);

View File

@ -111,7 +111,7 @@ const displayedScenarios = scenarios.slice(0, maxDisplayed);
clickedChip.classList.add('active');
}
window.scrollToElementById('tools-grid');
window.scrollToElementById('meToCo-grid');
};
window.toggleAllScenarios = function() {
@ -159,7 +159,7 @@ const displayedScenarios = scenarios.slice(0, maxDisplayed);
const gridToggle = document.querySelector('.view-toggle[data-view="grid"]');
if (gridToggle) {
gridToggle.click();
setTimeout(() => window.scrollToElementById('tools-grid'), 200);
setTimeout(() => window.scrollToElementById('meToCo-grid'), 200);
}
}
});

View File

@ -5,13 +5,13 @@ const data = await getToolsData();
const domains = data.domains;
const phases = data.phases;
const skillLevels = [...new Set(data.tools.map(tool => tool.skillLevel))].filter(Boolean).sort();
const platforms = [...new Set(data.tools.flatMap(tool => tool.platforms || []))].filter(Boolean).sort();
const licenses = [...new Set(data.tools.map(tool => tool.license))].filter(Boolean).sort();
const toolTypes = [...new Set(data.tools.map(tool => tool.type))].filter(Boolean).sort();
const accessTypes = [...new Set(data.tools.map(tool => tool.accessType))].filter(Boolean).sort();
const skillLevels = [...new Set(data.meToCo.map(tool => tool.skillLevel))].filter(Boolean).sort();
const platforms = [...new Set(data.meToCo.flatMap(tool => tool.platforms || []))].filter(Boolean).sort();
const licenses = [...new Set(data.meToCo.map(tool => tool.license))].filter(Boolean).sort();
const toolTypes = [...new Set(data.meToCo.map(tool => tool.type))].filter(Boolean).sort();
const accessTypes = [...new Set(data.meToCo.map(tool => tool.accessType))].filter(Boolean).sort();
const tagFrequency = data.tools.reduce((acc: Record<string, number>, tool: any) => {
const tagFrequency = data.meToCo.reduce((acc: Record<string, number>, tool: any) => {
tool.tags.forEach((tag: string) => {
acc[tag] = (acc[tag] || 0) + 1;
});
@ -282,8 +282,8 @@ const sortedTags = Object.entries(tagFrequency)
</div>
</div>
<script define:vars={{ toolsData: data.tools, tagFrequency, sortedTags }}>
window.toolsData = toolsData;
<script define:vars={{ meToCoData: data.meToCo, tagFrequency, sortedTags }}>
window.meToCoData = meToCoData;
document.addEventListener('DOMContentLoaded', () => {
const elements = {
@ -488,7 +488,7 @@ const sortedTags = Object.entries(tagFrequency)
}
function updateResultsCounter(count) {
const total = window.toolsData.length;
const total = window.meToCoData.length;
elements.resultsCounter.textContent = count === total
? `${total} Tools`
: `${count} von ${total} Tools`;
@ -508,7 +508,7 @@ const sortedTags = Object.entries(tagFrequency)
const activePhase = selectedPhaseFromSelect || selectedPhase;
const filtered = window.toolsData.filter(tool => {
const filtered = window.meToCoData.filter(tool => {
if (searchTerm && !(
tool.name.toLowerCase().includes(searchTerm) ||
tool.description.toLowerCase().includes(searchTerm) ||
@ -566,7 +566,7 @@ const sortedTags = Object.entries(tagFrequency)
updateResultsCounter(finalResults.length);
window.dispatchEvent(new CustomEvent('toolsFiltered', { detail: finalResults }));
window.dispatchEvent(new CustomEvent('meToCoFiltered', { detail: finalResults }));
}
function resetPrimaryFilters() {
@ -652,8 +652,8 @@ const sortedTags = Object.entries(tagFrequency)
window.dispatchEvent(new CustomEvent('viewChanged', { detail: view }));
if (view === 'hosted') {
const hosted = window.toolsData.filter(tool => isToolHosted(tool));
window.dispatchEvent(new CustomEvent('toolsFiltered', { detail: hosted }));
const hosted = window.meToCoData.filter(tool => isToolHosted(tool));
window.dispatchEvent(new CustomEvent('meToCoFiltered', { detail: hosted }));
} else {
filterTools();
}

View File

@ -6,12 +6,12 @@ const data = await getToolsData();
const domains = data.domains;
const phases = data.phases;
const tools = data.tools;
const meToCo = data.meToCo;
const domainAgnosticSoftware = data['domain-agnostic-software'] || [];
const domainAgnosticTools = domainAgnosticSoftware.map((section: any) => ({
section,
tools: tools.filter((tool: any) =>
meToCo: meToCo.filter((tool: any) =>
tool['domain-agnostic-software'] && tool['domain-agnostic-software'].includes(section.id)
)
}));
@ -20,7 +20,7 @@ const matrix: Record<string, Record<string, any[]>> = {};
domains.forEach((domain: any) => {
matrix[domain.id] = {};
phases.forEach((phase: any) => {
matrix[domain.id][phase.id] = tools.filter((tool: any) =>
matrix[domain.id][phase.id] = meToCo.filter((tool: any) =>
tool.type !== 'concept' &&
tool.domains && tool.domains.includes(domain.id) &&
tool.phases && tool.phases.includes(phase.id)
@ -42,7 +42,7 @@ domains.forEach((domain: any) => {
<h3 class="m-0 text-lg text-accent">
{sectionData.section.name}
<span id={`count-${sectionData.section.id}`} class="badge text-xs" style="background-color: var(--color-text-secondary); color: var(--color-bg); margin-left: 0.5rem;">
{sectionData.tools.length}
{sectionData.meToCo.length}
</span>
</h3>
<div class="collaboration-expand-icon">
@ -52,8 +52,8 @@ domains.forEach((domain: any) => {
</div>
</div>
<div class="collaboration-content hidden">
<div class="collaboration-tools-compact" id={`domain-agnostic-tools-${sectionData.section.id}`}>
{sectionData.tools.map((tool: any) => {
<div class="collaboration-meToCo-compact" id={`domain-agnostic-meToCo-${sectionData.section.id}`}>
{sectionData.meToCo.map((tool: any) => {
const hasValidProjectUrl = tool.projectUrl !== undefined &&
tool.projectUrl !== null &&
tool.projectUrl !== "" &&
@ -192,7 +192,7 @@ domains.forEach((domain: any) => {
<div id="tool-links-secondary" class="flex flex-col gap-2"></div>
</div>
<script define:vars={{ toolsData: tools, domainAgnosticSoftware, domainAgnosticTools }}>
<script define:vars={{ meToCoData: meToCo, domainAgnosticSoftware, domainAgnosticTools }}>
function getSelectedPhase() {
const activePhaseChip = document.querySelector('.phase-chip.active');
return activePhaseChip ? activePhaseChip.getAttribute('data-phase') : '';
@ -443,7 +443,7 @@ domains.forEach((domain: any) => {
window.toggleDomainAgnosticSection = toggleDomainAgnosticSection;
window.showToolDetails = function(toolName, modalType = 'primary') {
const tool = toolsData.find(t => t.name === toolName);
const tool = meToCoData.find(t => t.name === toolName);
if (!tool) {
console.error('Tool not found:', toolName);
return;
@ -523,7 +523,7 @@ domains.forEach((domain: any) => {
const relatedSoftware = tool.related_software || [];
if (relatedConcepts.length > 0 && modalType === 'primary') {
const conceptLinks = relatedConcepts.map(conceptName => {
const concept = toolsData.find(t => t.name === conceptName && t.type === 'concept');
const concept = meToCoData.find(t => t.name === conceptName && t.type === 'concept');
if (concept) {
return `<button class="tag cursor-pointer" style="background-color: var(--color-concept-bg); border: 1px solid var(--color-concept); color: var(--color-concept); transition: var(--transition-fast);"
onclick="event.stopPropagation(); window.showToolDetails('${conceptName}', 'secondary')"
@ -559,7 +559,7 @@ domains.forEach((domain: any) => {
if (relatedSoftware.length > 0 && modalType === 'primary') {
const softwareLinks = relatedSoftware.map(softwareName => {
const software = toolsData.find(t => t.name === softwareName && (t.type === 'software' || t.type === 'method'));
const software = meToCoData.find(t => t.name === softwareName && (t.type === 'software' || t.type === 'method'));
if (software) {
const isHosted = window.isToolHosted(software);
const isSoftwareMethod = software.type === 'method';
@ -776,14 +776,14 @@ domains.forEach((domain: any) => {
}
});
window.addEventListener('toolsFiltered', (event) => {
window.addEventListener('meToCoFiltered', (event) => {
const currentView = document.querySelector('.view-toggle.active')?.getAttribute('data-view');
if (currentView === 'matrix') {
setTimeout(updateMatrixHighlighting, 50);
}
});
window.addEventListener('toolsFiltered', (event) => {
window.addEventListener('meToCoFiltered', (event) => {
const filtered = event.detail;
const currentView = document.querySelector('.view-toggle.active')?.getAttribute('data-view');
@ -795,7 +795,7 @@ domains.forEach((domain: any) => {
domainAgnosticSoftware.forEach(sectionData => {
const section = document.getElementById(`domain-agnostic-section-${sectionData.id}`);
const container = document.getElementById(`domain-agnostic-tools-${sectionData.id}`);
const container = document.getElementById(`domain-agnostic-meToCo-${sectionData.id}`);
if (!section || !container) return;
});

View File

@ -8,7 +8,7 @@ const knowledgebaseCollection = defineCollection({
last_updated: z.date(),
tool_name: z.string().optional(),
related_tools: z.array(z.string()).default([]),
related_meToCo: z.array(z.string()).default([]),
author: z.string().default('Anon'),
difficulty: z.enum(['novice', 'beginner', 'intermediate', 'advanced', 'expert']).optional(),

View File

@ -48,7 +48,7 @@ Open-Source Android Forensik bietet robuste Alternativen zu kommerziellen Lösun
- USB 3.0+ Anschlüsse
### Installation
1. Download von [SANS SIFT Workstation](https://www.sans.org/tools/sift-workstation/)
1. Download von [SANS SIFT Workstation](https://www.sans.org/meToCo/sift-workstation/)
2. VMware/VirtualBox Import der OVA-Datei
3. VM-Konfiguration: 8GB+ RAM, 4+ CPU-Kerne
@ -78,10 +78,10 @@ wget https://github.com/sleuthkit/autopsy/releases/latest
### Android Debug Bridge (ADB)
```bash
# Ubuntu/Debian
sudo apt install android-tools-adb android-tools-fastboot
sudo apt install android-meToCo-adb android-meToCo-fastboot
# Windows - Download Android Platform Tools
# https://developer.android.com/studio/releases/platform-tools
# https://developer.android.com/studio/releases/platform-meToCo
```
### ALEAPP Installation

View File

@ -1,4 +1,4 @@
tools:
meToCo:
- name: Autopsy
type: software
description: >-
@ -1051,7 +1051,7 @@ tools:
- path-finding
- bloom-visualization
- apoc-procedures
- import-tools
- import-meToCo
related_concepts:
- SQL
- name: QGIS
@ -1465,7 +1465,7 @@ tools:
- batch-processing
- cloud-artifacts
- community-driven
- free-tools
- free-meToCo
- scenario:windows-registry
- scenario:persistence
related_concepts:
@ -1711,7 +1711,7 @@ tools:
domain-agnostic-software: null
skillLevel: beginner
accessType: download
url: https://www.osforensics.com/tools/mount-disk-images.html
url: https://www.osforensics.com/meToCo/mount-disk-images.html
projectUrl: ''
license: Proprietary
knowledgebase: false
@ -2629,7 +2629,7 @@ tools:
- network-forensics
- mobile-forensics
skillLevel: intermediate
url: https://www.sans.org/tools/sift-workstation/
url: https://www.sans.org/meToCo/sift-workstation/
icon: 🧰
platforms:
- OS
@ -3109,7 +3109,7 @@ tools:
- examination
- analysis
skillLevel: advanced
url: https://github.com/microsoft/ics-forensics-tools
url: https://github.com/microsoft/ics-forensics-meToCo
icon: 🏭
platforms:
- Windows
@ -3349,8 +3349,8 @@ tools:
- name: Collabora Online
type: software
description: >-
Webbasierte OpenSourceOfficeSuite mit kompletter
DokumentenBearbeitung und LiveKollaboration.
Web-basierte Open-Source-Office-Suite mit kompletter
Dokumenten-Bearbeitung und Live-Kollaboration.
skillLevel: beginner
url: https://www.collaboraonline.com
icon: 📝
@ -3637,7 +3637,7 @@ tools:
platforms:
- Linux
accessType: download
license: AGPLv3
license: AGPL v3
knowledgebase: false
- name: Elasticsearch
type: software
@ -3744,7 +3744,7 @@ tools:
platforms:
- Linux
accessType: download
license: GPLv3
license: GPL v3
knowledgebase: false
- name: FOCA
type: software
@ -3778,7 +3778,7 @@ tools:
platforms:
- Windows
accessType: download
license: GPLv3
license: GPL v3
knowledgebase: false
- name: Firmware Analysis Toolkit
type: software
@ -3817,8 +3817,8 @@ tools:
- name: GCHQ Tools
type: software
description: >-
Sammlung freier GCHQUtilities, allen voran CyberChef\_ das
„CyberSchweizerTaschenmesser“ für Encoding, Crypto und Datenanalyse.
Sammlung freier GCHQ-Utilities, allen voran CyberChef\_ das
„Cyber-Schweizer-Taschenmesser“ für Encoding, Crypto und Datenanalyse.
skillLevel: beginner
url: https://gchq.github.io/CyberChef
icon: 🥄
@ -3833,7 +3833,7 @@ tools:
platforms:
- Web
accessType: download
license: Apache2.0
license: Apache 2.0
knowledgebase: false
- name: Gephi
type: software
@ -4073,7 +4073,7 @@ tools:
- Linux
- Android
accessType: download
license: GPLv2
license: GPL v2
knowledgebase: false
- name: Loki
type: software
@ -4107,7 +4107,7 @@ tools:
- Windows
- Linux
accessType: download
license: GPLv3
license: GPL v3
knowledgebase: false
- name: Maltego
type: software
@ -4212,7 +4212,7 @@ tools:
platforms:
- Linux
accessType: download
license: AGPLv3
license: AGPL v3
knowledgebase: false
- name: Oxygen Forensic Suite
type: software
@ -4278,7 +4278,7 @@ tools:
- Linux
- macOS
accessType: download
license: LGPLv3
license: LGPL v3
knowledgebase: false
- name: Rekall
type: software
@ -4313,7 +4313,7 @@ tools:
- Linux
- macOS
accessType: download
license: Apache2.0
license: Apache 2.0
knowledgebase: false
- name: Suricata
type: software
@ -4347,7 +4347,7 @@ tools:
- Linux
- Windows
accessType: download
license: GPLv2
license: GPL v2
knowledgebase: false
- name: VirusTotal
type: software
@ -4443,7 +4443,7 @@ tools:
platforms:
- Windows
accessType: download
license: GPLv2
license: GPL v2
knowledgebase: false
- name: Zeek
type: software
@ -4511,7 +4511,7 @@ tools:
- Windows
- macOS
accessType: download
license: Apache2.0
license: Apache 2.0
knowledgebase: false
- name: tcpdump
type: software
@ -4578,20 +4578,20 @@ tools:
platforms:
- Windows
accessType: download
license: GPLv3
license: GPL v3
knowledgebase: false
- name: grep
type: software
description: >-
Klassisches Unix-Werkzeug zur Zeilenfilterung, entwickelt1973 von
KenThompson zur schnellen Suche nach regulären Ausdrücken in Dateien oder
Datenströmen. Der Name leitet sich vom edKommando „g/re/p“ ab und
Klassisches Unix-Werkzeug zur Zeilenfilterung, entwickelt 1973 von
Ken Thompson zur schnellen Suche nach regulären Ausdrücken in Dateien oder
Datenströmen. Der Name leitet sich vom ed-Kommando „g/re/p“ ab und
spiegelt die Funktionsweise wider: globaler Ausdruck, Zeile drucken.
GNUgrep unterstützt drei RegexDialekte (BRE,ERE,PCRE),
FarbHighlighting und ZeroCopyBlockPufferung für Höchstgeschwindigkeit
auch in riesigen Logs. Dank seiner PipeKompatibilität ist grep
Grundbaustein unzähliger DFIREinzeiler zum Parsen von Artefakten,
NetzwerkFlows und MemoryDumps.
GNU grep unterstützt drei Regex-Dialekte (BRE, ERE, PCRE),
Farb-Highlighting und Zero-Copy-Block-Pufferung für Höchstgeschwindigkeit
auch in riesigen Logs. Dank seiner Pipe-Kompatibilität ist grep
Grundbaustein unzähliger DFIR-Einzeiler zum Parsen von Artefakten,
Netzwerk-Flows und Memory-Dumps.
skillLevel: beginner
url: https://www.gnu.org/software/grep/
icon: 🔍
@ -4618,12 +4618,12 @@ tools:
- name: md5sum / sha256sum
type: software
description: >-
Klassische KommandozeilenChecksummengeneratoren aus den GNUcoreutils,
die schnell kryptografische Prüfsummen (MD5 bzw. SHA256) berechnen und
verifizieren können. Ideal zur Integritätsprüfung von ForensikImages,
LogArchiven und DownloadArtefakten; auch als StreamFilter einsetzbar
Klassische Kommandozeilen-Checksummengeneratoren aus den GNU coreutils,
die schnell kryptografische Prüfsummen (MD5 bzw. SHA-256) berechnen und
verifizieren können. Ideal zur Integritätsprüfung von Forensik-Images,
Log-Archiven und Download-Artefakten; auch als Stream-Filter einsetzbar
(„cat image.dd | sha256sum“). Minimalistischer Funktionsumfang, aber auf
nahezu jeder Unixähnlichen Plattform vorinstalliert und damit überall
nahezu jeder Unix-ähnlichen Plattform vorinstalliert und damit überall
verfügbar.
skillLevel: beginner
url: https://www.gnu.org/software/coreutils/
@ -4651,11 +4651,11 @@ tools:
- name: hashdeep
type: software
description: >-
Multithreadfähiges AuditWerkzeug, das Dateien rekursiv einliest und
gleich mehrere HashAlgorithmen (MD5, SHA1, SHA256, Tiger u.a.) erzeugt.
Multithread-fähiges Audit-Werkzeug, das Dateien rekursiv einliest und
gleich mehrere Hash-Algorithmen (MD5, SHA-1, SHA-256, Tiger u.a.) erzeugt.
Unterstützt „baseline auditing“ zum automatischen Wieder­erkennen neuer
oder veränderter Dateien und kann HashListen in NISTNSRL oder
CSVFormat ausgeben perfekt für große DatenträgerBatches.
oder veränderter Dateien und kann Hash-Listen in NIST-NSRL- oder
CSV-Format ausgeben perfekt für große Datenträger-Batches.
skillLevel: intermediate
url: https://github.com/jessek/hashdeep
icon: 🏷️
@ -4682,9 +4682,9 @@ tools:
description: >-
Tool zur Berechnung „fuzzy hashes“ (Context Triggered Piecewise Hashing,
CTPH) für Ähnlichkeits­analysen von Dateien. Kann binäre
MalwareVarianten, Dokumentschablonen oder Logs selbst bei nur teilweisen
Überschneidungen matchen. AusgabeHashes lassen sich in Datenbanken oder
YARARegeln integrieren.
Malware-Varianten, Dokumentschablonen oder Logs selbst bei nur teilweisen
Überschneidungen matchen. Ausgabe-Hashes lassen sich in Datenbanken oder
YARA-Regeln integrieren.
skillLevel: intermediate
url: https://ssdeep-project.github.io/ssdeep/
icon: 🔍
@ -4707,12 +4707,12 @@ tools:
- name: hashcat
type: software
description: >-
Höchstperformanter PasswortRecoveryAccelerator, der CPUs, GPUs, FPGAs
und sogar ASICs parallel nutzen kann. Unterstützt über 300 HashFormate
(u.a. bcrypt, NTLM, Kerberos, WPAPMKID) und besitzt flexible Angriffsmodi
(Dictionary, Mask, Hybrid, RuleBased). Dank OpenCLBackend skaliert
hashcat von LaptopGPU bis zu verteilten Clustern unverzichtbar für
CredentialAudits und IncidentResponse.
Höchstperformanter Passwort-Recovery-Accelerator, der CPUs, GPUs, FPGAs
und sogar ASICs parallel nutzen kann. Unterstützt über 300 Hash-Formate
(u.a. bcrypt, NTLM, Kerberos, WPA-PMKID) und besitzt flexible Angriffsmodi
(Dictionary, Mask, Hybrid, Rule-Based). Dank OpenCL-Backend skaliert
hashcat von Laptop-GPU bis zu verteilten Clustern unverzichtbar für
Credential-Audits und Incident-Response.
skillLevel: advanced
url: https://hashcat.net/hashcat/
icon:
@ -4737,10 +4737,10 @@ tools:
- name: Linkurious
type: software
description: >-
Intuitive GraphVisualisierungs und Analyseplattform, die
Intuitive Graph-Visualisierungs- und Analyseplattform, die
Graphdatenbanken wie Neo4j überlagert und Ermittler:innen dabei
unterstützt, versteckte Beziehungen in Betrugs, AML und
Sicherheitsfällen aufzudecken. Leistungsstarke Filter, Geo und
unterstützt, versteckte Beziehungen in Betrugs-, AML- und
Sicherheitsfällen aufzudecken. Leistungsstarke Filter, Geo- und
Zeitachsenansichten sowie Automatisierungsvorlagen erleichtern das Hunten
komplexer Netzwerke und das Erstellen aussagekräftiger Beweisgrafiken.
skillLevel: intermediate
@ -4767,7 +4767,7 @@ tools:
knowledgebase: false
- name: Android Studio
type: software
description: "Offizielle IDE von Google für die AndroidEntwicklung, basierend auf JetBrains\_IntelliJ\_IDEA. Integriert ein flexibles GradleBuildsystem, JetpackComposeDesigner, GeräteEmulator, Profiler und DebuggingTools, um mobile Apps effizient zu erstellen, zu testen und zu analysieren auch für forensische RePackaging oder MalwareAnalysen von APKs."
description: "Offizielle IDE von Google für die Android-Entwicklung, basierend auf JetBrains\_IntelliJ\_IDEA. Integriert ein flexibles Gradle-Buildsystem, Jetpack-Compose-Designer, Geräte-Emulator, Profiler und Debugging-Tools, um mobile Apps effizient zu erstellen, zu testen und zu analysieren auch für forensische Re-Packaging- oder Malware-Analysen von APKs."
skillLevel: intermediate
url: https://developer.android.com/studio
icon: 📱
@ -4793,13 +4793,13 @@ tools:
- name: ADB
type: software
description: >-
Vielseitiges ClientServerKommandozeilenwerkzeug, mit dem Fachleute über
USB oder Netzwerk mit AndroidGeräten und Emulatoren kommunizieren: Pakete
Vielseitiges Client-Server-Kommandozeilenwerkzeug, mit dem Fachleute über
USB oder Netzwerk mit Android-Geräten und Emulatoren kommunizieren: Pakete
installieren, Dateien extrahieren, Logcats erfassen, Ports weiterleiten
und logische wie physische Datensicherungen erstellen. Unverzichtbar für
mobile Forensik, IncidentResponse und AppEntwicklung.
mobile Forensik, Incident-Response und App-Entwicklung.
skillLevel: intermediate
url: https://developer.android.com/tools/adb
url: https://developer.android.com/meToCo/adb
icon: 🔌
domains:
- mobile-forensics
@ -4822,7 +4822,7 @@ tools:
knowledgebase: false
- name: dc3dd
type: software
description: "dc3dd ist eine forensisch erweiterte Variante des klassischen UnixBefehls dd. Sie unterstützt das gleichzeitige Berechnen mehrerer HashWerte (MD5,SHA1,SHA256,SHA512) während der ImageErstellung, führt ein detailliertes Log mit Prüfsummen und Fehlerblöcken und zeigt einen Fortschrittsbalken an. Weitere Funktionen sind das Splitten großer Abbilder, das gezielte Überschreiben von Mustern zum sicheren Löschen\_und die Möglichkeit, Fehlersektoren separat auszugeben, was besonders bei defekten Medien hilfreich ist."
description: "dc3dd ist eine forensisch erweiterte Variante des klassischen Unix-Befehls dd. Sie unterstützt das gleichzeitige Berechnen mehrerer Hash-Werte (MD5, SHA-1, SHA-256, SHA-512) während der Image-Erstellung, führt ein detailliertes Log mit Prüfsummen und Fehlerblöcken und zeigt einen Fortschrittsbalken an. Weitere Funktionen sind das Splitten großer Abbilder, das gezielte Überschreiben von Mustern zum sicheren Löschen\_und die Möglichkeit, Fehlersektoren separat auszugeben, was besonders bei defekten Medien hilfreich ist."
skillLevel: intermediate
url: https://sourceforge.net/projects/dc3dd/
icon: 💾
@ -4848,15 +4848,15 @@ tools:
- name: ddrescue
type: software
description: >-
GNUddrescue ist ein spezialisiertes DatenrettungsTool, das beschädigte
GNU ddrescue ist ein spezialisiertes Datenrettungs-Tool, das beschädigte
oder fehlerhafte Blockgeräte sektorweise ausliest und zunächst alle gut
lesbaren Bereiche sichert, bevor es sich mehrfach an problematischen
Sektoren versucht. Sein MapFile protokolliert jeden Leseversuch, sodass
Sektoren versucht. Sein Map-File protokolliert jeden Leseversuch, sodass
abgebrochene Wiederherstellungen jederzeit fortgesetzt oder optimiert
werden können, ohne bereits gerettete Sektoren erneut zu belasten. Zudem
bietet ddrescue einen FillModus, um schwierige Bereiche gezielt mit
bietet ddrescue einen Fill-Modus, um schwierige Bereiche gezielt mit
Platzhaltern zu überschreiben nützlich für die Vorbereitung
nachträglicher DateisystemReparaturen.
nachträglicher Dateisystem-Reparaturen.
skillLevel: intermediate
url: https://www.gnu.org/software/ddrescue/
icon: 🛟
@ -4881,7 +4881,7 @@ tools:
knowledgebase: false
- name: REMnux
type: software
description: "REMnux ist eine auf Ubuntu basierende LinuxDistribution, die eine kuratierte Sammlung frei verfügbarer Tools für MalwareAnalyse, Reverse\_Engineering und Speicherforensik in einer sofort einsatzbereiten VM vereint. Sie erspart Analyst:innen das mühsame Zusammenstellen einzelner Werkzeuge, da Paketverwaltung, DesktopIntegration und LabSkripte bereits vorkonfiguriert sind. Zu den integrierten Highlights zählen unter anderem Ghidra, Radare2, Volatility, stegdetect sowie diverse Netzwerk und SandboxingUtilities, die regelmäßig in koordinierten Releases aktualisiert werden."
description: "REMnux ist eine auf Ubuntu basierende Linux-Distribution, die eine kuratierte Sammlung frei verfügbarer Tools für Malware-Analyse, Reverse\_Engineering und Speicherforensik in einer sofort einsatzbereiten VM vereint. Sie erspart Analyst:innen das mühsame Zusammenstellen einzelner Werkzeuge, da Paketverwaltung, Desktop-Integration und Lab-Skripte bereits vorkonfiguriert sind. Zu den integrierten Highlights zählen unter anderem Ghidra, Radare2, Volatility, stegdetect sowie diverse Netzwerk- und Sandboxing-Utilities, die regelmäßig in koordinierten Releases aktualisiert werden."
skillLevel: intermediate
url: https://remnux.org/
icon: 🐧
@ -4905,7 +4905,7 @@ tools:
knowledgebase: false
- name: Android Backup Extractor
type: software
description: "Das bewährte KommandozeilenWerkzeug zum Entpacken und Packen von AndroidBackups (.ab) aus »adb backup«\_ inklusive Entschlüsselung passwort­geschützter Archive dank integrierter AESRoutinen. Ideal für die Extraktion forensisch relevanter AppDaten ohne RootZugriff und damit ein Musthave für MobileAnalysen. :contentReference[oaicite:0]{index=0}"
description: "Das bewährte Kommandozeilen-Werkzeug zum Entpacken und Packen von Android-Backups (.ab) aus »adb backup«\_ inklusive Entschlüsselung passwort­geschützter Archive dank integrierter AES-Routinen. Ideal für die Extraktion forensisch relevanter App-Daten ohne Root-Zugriff und damit ein Must-have für Mobile-Analysen. :contentReference[oaicite:0]{index=0}"
skillLevel: intermediate
url: https://github.com/nelenkov/android-backup-extractor
icon: 📦
@ -4932,7 +4932,7 @@ tools:
knowledgebase: false
- name: CAINE
type: software
description: "CAINE (Computer Aided INvestigative Environment) ist eine Ubuntubasierte LiveLinuxDistribution, die mehr als 150 ForensikWerkzeuge in einer schreibgeschützten Umgebung bündelt. Version 14.0 »Lightstream« (März\_2025) bringt Kernel\_6.8, UEFISupport und das UnBlockGUI zum gezielten Aufheben der WriteBlockFunktion für einzelne Devices. :contentReference[oaicite:2]{index=2}"
description: "CAINE (Computer Aided INvestigative Environment) ist eine Ubuntu-basierte Live-Linux-Distribution, die mehr als 150 Forensik-Werkzeuge in einer schreibgeschützten Umgebung bündelt. Version 14.0 »Lightstream« (März\_2025) bringt Kernel\_6.8, UEFI-Support und das UnBlock-GUI zum gezielten Aufheben der Write-Block-Funktion für einzelne Devices. :contentReference[oaicite:2]{index=2}"
skillLevel: beginner
url: https://www.caine-live.net/
icon: 💿
@ -5002,7 +5002,7 @@ tools:
knowledgebase: false
- name: WiFi Pineapple
type: software
description: "Die Hak5Hardware liefert mit der patentierten PineAPSuite einen vollwertigen RogueAccessPoint: automatisches Recon, Handshake und EnterpriseCredentialCapture, gezielte ClientFilterung sowie Cloud\_C²Fernsteuerung\_ alles per browser­basierter GUI auf dem Mark\_VII oder EnterpriseModell. :contentReference[oaicite:6]{index=6}"
description: "Die Hak5-Hardware liefert mit der patentierten PineAP-Suite einen vollwertigen Rogue-Access-Point: automatisches Recon, Handshake- und Enterprise-Credential-Capture, gezielte Client-Filterung sowie Cloud\_C²-Fernsteuerung\_ alles per browser­basierter GUI auf dem Mark\_VII oder Enterprise-Modell. :contentReference[oaicite:6]{index=6}"
skillLevel: intermediate
url: https://shop.hak5.org/products/wifi-pineapple
icon: 📡

View File

@ -1,4 +1,4 @@
tools:
meToCo:
- name: Autopsy
type: software
description: >-
@ -113,7 +113,7 @@ phases:
domain-agnostic-software:
- id: collaboration-general
name: Übergreifend & Kollaboration
description: Cross-cutting tools and collaboration platforms
description: Cross-cutting meToCo and collaboration platforms
- id: specific-os
name: Betriebssysteme
description: Operating Systems which focus on forensics

4
src/env.d.ts vendored
View File

@ -7,7 +7,7 @@ declare global {
toggleTheme: () => void;
getStoredTheme: () => string;
};
toolsData: any[];
meToCoData: any[];
showToolDetails: (toolName: string, modalType?: string) => void;
hideToolDetails: (modalType?: string) => void;
hideAllToolDetails: () => void;
@ -19,7 +19,7 @@ declare global {
clearAllFilters?: () => void;
createToolSlug: (toolName: string) => string;
findToolByIdentifier: (tools: any[], identifier: string) => any | undefined;
findToolByIdentifier: (meToCo: any[], identifier: string) => any | undefined;
isToolHosted: (tool: any) => boolean;
checkClientAuth: (context?: string) => Promise<{authenticated: boolean; authRequired: boolean; expires?: string}>;

View File

@ -8,7 +8,7 @@ export interface Props {
description?: string;
}
const { title, description = 'ForensicPathways - A comprehensive directory of digital forensics and incident response tools' } = Astro.props;
const { title, description = 'ForensicPathways - A comprehensive directory of digital forensics and incident response meToCo' } = Astro.props;
---
<!DOCTYPE html>
@ -34,10 +34,10 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
.replace(/^-|-$/g, ''); // Remove leading/trailing hyphens
}
function findToolByIdentifier(tools, identifier) {
if (!identifier || !Array.isArray(tools)) return undefined;
function findToolByIdentifier(meToCo, identifier) {
if (!identifier || !Array.isArray(meToCo)) return undefined;
return tools.find(tool =>
return meToCo.find(tool =>
tool.name === identifier ||
createToolSlug(tool.name) === identifier.toLowerCase()
);
@ -76,14 +76,14 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
scrollToElement(element, options);
}
function prioritizeSearchResults(tools, searchTerm) {
function prioritizeSearchResults(meToCo, searchTerm) {
if (!searchTerm || !searchTerm.trim()) {
return tools;
return meToCo;
}
const lowerSearchTerm = searchTerm.toLowerCase().trim();
return tools.sort((a, b) => {
return meToCo.sort((a, b) => {
const aTagsLower = (a.tags || []).map(tag => tag.toLowerCase());
const bTagsLower = (b.tags || []).map(tag => tag.toLowerCase());

View File

@ -84,10 +84,10 @@ async function validateToolData(tool: any, action: string): Promise<{ valid: boo
const errors: string[] = [];
try {
const existingData = { tools: [] };
const existingData = { meToCo: [] };
if (action === 'add') {
const existingNames = new Set(existingData.tools.map((t: any) => t.name.toLowerCase()));
const existingNames = new Set(existingData.meToCo.map((t: any) => t.name.toLowerCase()));
if (existingNames.has(tool.name.toLowerCase())) {
errors.push('A tool with this name already exists');
}
@ -102,10 +102,10 @@ async function validateToolData(tool: any, action: string): Promise<{ valid: boo
}
} else if (tool.type === 'software') {
if (!tool.platforms || tool.platforms.length === 0) {
errors.push('Software tools must specify at least one platform');
errors.push('Software meToCo must specify at least one platform');
}
if (!tool.license) {
errors.push('Software tools must specify a license');
errors.push('Software meToCo must specify a license');
}
}

View File

@ -14,7 +14,7 @@ if (authResult instanceof Response) {
const { authenticated, userEmail, userId } = authResult;
const data = await getToolsData();
const sortedTools = data.tools.sort((a: any, b: any) => a.name.localeCompare(b.name));
const sortedTools = data.meToCo.sort((a: any, b: any) => a.name.localeCompare(b.name));
---
<BaseLayout title="Contribute Knowledge Base Article">

View File

@ -17,7 +17,7 @@ const data = await getToolsData();
const domains = data.domains;
const phases = data.phases;
const domainAgnosticSoftware = data['domain-agnostic-software'] || [];
const existingTools = data.tools;
const existingTools = data.meToCo;
const editToolName = Astro.url.searchParams.get('edit');
const editTool = editToolName ? existingTools.find(tool => tool.name === editToolName) : null;

View File

@ -8,7 +8,7 @@ import TargetedScenarios from '../components/TargetedScenarios.astro';
import { getToolsData } from '../utils/dataService.js';
const data = await getToolsData();
const tools = data.tools;
const meToCo = data.meToCo;
const phases = data.phases;
---
@ -116,7 +116,7 @@ const phases = data.phases;
<div class="nist-workflow">
{phases.map((phase: any, index: number) => {
const phaseTools = tools.filter((tool: any) =>
const phaseTools = meToCo.filter((tool: any) =>
tool.phases && tool.phases.includes(phase.id)
);
@ -155,23 +155,23 @@ const phases = data.phases;
<AIQueryInterface />
<section id="tools-grid" style="padding-bottom: 2rem;">
<div class="grid-auto-fit" id="tools-container">
{tools.map((tool: any) => (
<section id="meToCo-grid" style="padding-bottom: 2rem;">
<div class="grid-auto-fit" id="meToCo-container">
{meToCo.map((tool: any) => (
<ToolCard tool={tool} />
))}
</div>
<div id="no-results" style="display: none; text-align: center; padding: 4rem 0;">
<p class="text-muted" style="font-size: 1.125rem;">No tools found matching your criteria.</p>
<p class="text-muted" style="font-size: 1.125rem;">No meToCo found matching your criteria.</p>
</div>
</section>
<ToolMatrix data={data} />
</BaseLayout>
<script define:vars={{ toolsData: data.tools, phases: data.phases }}>
window.toolsData = toolsData;
<script define:vars={{ meToCoData: data.meToCo, phases: data.phases }}>
window.meToCoData = meToCoData;
window.selectApproach = function(approach) {
console.log(`Selected approach: ${approach}`);
@ -221,19 +221,19 @@ const phases = data.phases;
gridToggle.click();
}
window.scrollToElementById('tools-grid');
window.scrollToElementById('meToCo-grid');
};
document.addEventListener('DOMContentLoaded', () => {
const toolsContainer = document.getElementById('tools-container');
const toolsGrid = document.getElementById('tools-grid');
const meToCoContainer = document.getElementById('meToCo-container');
const meToCoGrid = document.getElementById('meToCo-grid');
const matrixContainer = document.getElementById('matrix-container');
const aiInterface = document.getElementById('ai-interface');
const filtersSection = document.getElementById('filters-section');
const noResults = document.getElementById('no-results');
const aiQueryBtn = document.getElementById('ai-query-btn');
if (!toolsContainer || !toolsGrid || !matrixContainer || !noResults || !aiInterface || !filtersSection) {
if (!meToCoContainer || !meToCoGrid || !matrixContainer || !noResults || !aiInterface || !filtersSection) {
console.error('Required DOM elements not found');
return;
}
@ -250,7 +250,7 @@ const phases = data.phases;
}
function switchToView(view) {
toolsGrid.style.display = 'none';
meToCoGrid.style.display = 'none';
matrixContainer.style.display = 'none';
aiInterface.style.display = 'none';
filtersSection.style.display = 'none';
@ -279,7 +279,7 @@ const phases = data.phases;
showFilterControls();
break;
default:
toolsGrid.style.display = 'block';
meToCoGrid.style.display = 'block';
filtersSection.style.display = 'block';
showFilterControls();
break;
@ -357,7 +357,7 @@ const phases = data.phases;
}, 2000);
} else {
console.warn('Tool card not found in grid:', toolName);
window.scrollToElementById('tools-grid');
window.scrollToElementById('meToCo-grid');
}
}, 300);
}, 200);
@ -412,7 +412,7 @@ const phases = data.phases;
return;
}
const tool = window.findToolByIdentifier(window.toolsData, toolParam);
const tool = window.findToolByIdentifier(window.meToCoData, toolParam);
if (!tool) {
console.warn('Shared tool not found:', toolParam);
return;
@ -442,7 +442,7 @@ const phases = data.phases;
}, 100);
}
window.addEventListener('toolsFiltered', (event) => {
window.addEventListener('meToCoFiltered', (event) => {
const filtered = event.detail;
const currentView = document.querySelector('.view-toggle.active')?.getAttribute('data-view');

View File

@ -11,7 +11,7 @@ const allKnowledgebaseEntries = await getCollection('knowledgebase', (entry) =>
const knowledgebaseEntries = allKnowledgebaseEntries.map((entry) => {
const associatedTool = entry.data.tool_name
? data.tools.find((tool: any) => tool.name === entry.data.tool_name)
? data.meToCo.find((tool: any) => tool.name === entry.data.tool_name)
: null;
return {
@ -25,7 +25,7 @@ const knowledgebaseEntries = allKnowledgebaseEntries.map((entry) => {
tags: entry.data.tags || [],
tool_name: entry.data.tool_name,
related_tools: entry.data.related_tools || [],
related_meToCo: entry.data.related_meToCo || [],
associatedTool,
name: entry.data.title,
@ -41,7 +41,7 @@ const knowledgebaseEntries = allKnowledgebaseEntries.map((entry) => {
knowledgebaseEntries.sort((a: any, b: any) => a.title.localeCompare(b.title));
---
<BaseLayout title="Knowledgebase" description="Extended documentation and insights for DFIR tools">
<BaseLayout title="Knowledgebase" description="Extended documentation and insights for DFIR meToCo">
<section class="section-padding">
<div class="text-center mb-8 p-8 bg-secondary rounded-lg border">
<h1 class="mb-4 text-2xl text-primary">Knowledgebase</h1>

View File

@ -25,12 +25,12 @@ const { Content } = await entry.render();
const data = await getToolsData();
const primaryTool = entry.data.tool_name
? data.tools.find((t: any) => t.name === entry.data.tool_name)
? data.meToCo.find((t: any) => t.name === entry.data.tool_name)
: null;
const relatedTools = entry.data.related_tools
? entry.data.related_tools.map((toolName: string) =>
data.tools.find((t: any) => t.name === toolName)
const relatedTools = entry.data.related_meToCo
? entry.data.related_meToCo.map((toolName: string) =>
data.meToCo.find((t: any) => t.name === toolName)
).filter(Boolean)
: [];

View File

@ -5,7 +5,7 @@ import { getToolsData } from '../utils/dataService.js';
const data = await getToolsData();
const hostedServices = data.tools.filter((tool: any) => {
const hostedServices = data.meToCo.filter((tool: any) => {
return tool.projectUrl !== undefined &&
tool.projectUrl !== null &&
tool.projectUrl !== "" &&

View File

@ -883,7 +883,7 @@ input[type="checkbox"] {
}
/* Collaboration Tools - Consolidated */
.collaboration-tools-compact {
.collaboration-meToCo-compact {
display: flex;
gap: 1rem;
flex-wrap: wrap;
@ -1107,7 +1107,7 @@ input[type="checkbox"] {
line-height: 1.4;
}
.phase-tools {
.phase-meToCo {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1rem;
@ -2051,9 +2051,9 @@ footer {
.phase-button { width: 100%; }
.phase-tools { grid-template-columns: 1fr; }
.phase-meToCo { grid-template-columns: 1fr; }
.collaboration-tools-compact {
.collaboration-meToCo-compact {
flex-direction: column;
}
.collaboration-tool-compact {

View File

@ -2065,7 +2065,7 @@ input[type="checkbox"] {
line-height: 1.4;
}
.phase-tools {
.phase-meToCo {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1rem;
@ -3123,7 +3123,7 @@ footer {
}
/* Collaboration Tools */
.collaboration-tools-compact {
.collaboration-meToCo-compact {
display: flex;
gap: 1rem;
flex-wrap: wrap;
@ -3538,11 +3538,11 @@ footer {
}
@media (width <= 480px) {
.phase-tools {
.phase-meToCo {
grid-template-columns: 1fr;
}
.collaboration-tools-compact {
.collaboration-meToCo-compact {
flex-direction: column;
}

View File

@ -127,8 +127,8 @@ class ImprovedMicroTaskAIPipeline {
console.log('[AI PIPELINE] Configuration loaded:', {
embeddingCandidates: this.embeddingCandidates,
embeddingSelection: `${this.embeddingSelectionLimit} tools, ${this.embeddingConceptsLimit} concepts`,
noEmbeddingsLimits: `${this.noEmbeddingsToolLimit || 'unlimited'} tools, ${this.noEmbeddingsConceptLimit || 'unlimited'} concepts`,
embeddingSelection: `${this.embeddingSelectionLimit} meToCo, ${this.embeddingConceptsLimit} concepts`,
noEmbeddingsLimits: `${this.noEmbeddingsToolLimit || 'unlimited'} meToCo, ${this.noEmbeddingsConceptLimit || 'unlimited'} concepts`,
auditEnabled: this.auditConfig.enabled
});
}
@ -352,7 +352,7 @@ class ImprovedMicroTaskAIPipeline {
return true;
}
private async getIntelligentCandidates(userQuery: string, toolsData: any, mode: string) {
private async getIntelligentCandidates(userQuery: string, meToCoData: any, mode: string) {
let candidateTools: any[] = [];
let candidateConcepts: any[] = [];
let selectionMethod = 'unknown';
@ -378,12 +378,12 @@ class ImprovedMicroTaskAIPipeline {
console.log(`[AI PIPELINE] Embeddings found ${similarItems.length} similar items`);
const toolsMap = new Map<string, any>(toolsData.tools.map((tool: any) => [tool.name, tool]));
const conceptsMap = new Map<string, any>(toolsData.concepts.map((concept: any) => [concept.name, concept]));
const meToCoMap = new Map<string, any>(meToCoData.meToCo.map((tool: any) => [tool.name, tool]));
const conceptsMap = new Map<string, any>(meToCoData.concepts.map((concept: any) => [concept.name, concept]));
const similarTools = similarItems
.filter((item): item is SimilarityResult => item.type === 'tool')
.map(item => toolsMap.get(item.name))
.map(item => meToCoMap.get(item.name))
.filter((tool): tool is any => tool !== undefined);
const similarConcepts = similarItems
@ -391,9 +391,9 @@ class ImprovedMicroTaskAIPipeline {
.map(item => conceptsMap.get(item.name))
.filter((concept): concept is any => concept !== undefined);
console.log(`[AI PIPELINE] Similarity-ordered results: ${similarTools.length} tools, ${similarConcepts.length} concepts`);
console.log(`[AI PIPELINE] Similarity-ordered results: ${similarTools.length} meToCo, ${similarConcepts.length} concepts`);
const totalAvailableTools = toolsData.tools.length;
const totalAvailableTools = meToCoData.meToCo.length;
const reductionRatio = similarTools.length / totalAvailableTools;
if (similarTools.length >= this.embeddingsMinTools && reductionRatio <= this.embeddingsMaxReductionRatio) {
@ -401,15 +401,15 @@ class ImprovedMicroTaskAIPipeline {
candidateConcepts = similarConcepts;
selectionMethod = 'embeddings_candidates';
console.log(`[AI PIPELINE] Using embeddings filtering: ${totalAvailableTools}${similarTools.length} tools (${(reductionRatio * 100).toFixed(1)}% reduction)`);
console.log(`[AI PIPELINE] Using embeddings filtering: ${totalAvailableTools}${similarTools.length} meToCo (${(reductionRatio * 100).toFixed(1)}% reduction)`);
} else {
if (similarTools.length < this.embeddingsMinTools) {
console.log(`[AI PIPELINE] Embeddings found too few tools (${similarTools.length} < ${this.embeddingsMinTools}), using full dataset`);
console.log(`[AI PIPELINE] Embeddings found too few meToCo (${similarTools.length} < ${this.embeddingsMinTools}), using full dataset`);
} else {
console.log(`[AI PIPELINE] Embeddings didn't filter enough (${(reductionRatio * 100).toFixed(1)}% > ${(this.embeddingsMaxReductionRatio * 100).toFixed(1)}%), using full dataset`);
}
candidateTools = toolsData.tools;
candidateConcepts = toolsData.concepts;
candidateTools = meToCoData.meToCo;
candidateConcepts = meToCoData.concepts;
selectionMethod = 'full_dataset';
}
@ -418,7 +418,7 @@ class ImprovedMicroTaskAIPipeline {
{ query: userQuery, threshold: this.similarityThreshold, candidates: this.embeddingCandidates },
{
candidatesFound: similarItems.length,
toolsInOrder: similarTools.slice(0, 3).map((t: any) => t.name),
meToCoInOrder: similarTools.slice(0, 3).map((t: any) => t.name),
conceptsInOrder: similarConcepts.slice(0, 3).map((c: any) => c.name),
reductionRatio: reductionRatio,
usingEmbeddings: selectionMethod === 'embeddings_candidates',
@ -437,20 +437,20 @@ class ImprovedMicroTaskAIPipeline {
}
} else {
console.log(`[AI PIPELINE] Embeddings disabled or not ready, using full dataset`);
candidateTools = toolsData.tools;
candidateConcepts = toolsData.concepts;
candidateTools = meToCoData.meToCo;
candidateConcepts = meToCoData.concepts;
selectionMethod = 'full_dataset';
}
console.log(`[AI PIPELINE] AI will analyze ${candidateTools.length} candidate tools (method: ${selectionMethod})`);
console.log(`[AI PIPELINE] AI will analyze ${candidateTools.length} candidate meToCo (method: ${selectionMethod})`);
const finalSelection = await this.aiSelectionWithFullData(userQuery, candidateTools, candidateConcepts, mode, selectionMethod);
return {
tools: finalSelection.selectedTools,
meToCo: finalSelection.selectedTools,
concepts: finalSelection.selectedConcepts,
domains: toolsData.domains,
phases: toolsData.phases,
'domain-agnostic-software': toolsData['domain-agnostic-software']
domains: meToCoData.domains,
phases: meToCoData.phases,
'domain-agnostic-software': meToCoData['domain-agnostic-software']
};
}
@ -463,7 +463,7 @@ class ImprovedMicroTaskAIPipeline {
) {
const selectionStart = Date.now();
const toolsWithFullData = candidateTools.map((tool: any) => ({
const meToCoWithFullData = candidateTools.map((tool: any) => ({
name: tool.name,
type: tool.type,
description: tool.description,
@ -492,14 +492,14 @@ class ImprovedMicroTaskAIPipeline {
related_software: concept.related_software || []
}));
let toolsToSend: any[];
let meToCoToSend: any[];
let conceptsToSend: any[];
if (selectionMethod === 'embeddings_candidates') {
toolsToSend = toolsWithFullData.slice(0, this.embeddingSelectionLimit);
meToCoToSend = meToCoWithFullData.slice(0, this.embeddingSelectionLimit);
conceptsToSend = conceptsWithFullData.slice(0, this.embeddingConceptsLimit);
console.log(`[AI PIPELINE] Embeddings enabled: sending top ${toolsToSend.length} similarity-ordered tools`);
console.log(`[AI PIPELINE] Embeddings enabled: sending top ${meToCoToSend.length} similarity-ordered meToCo`);
} else {
const maxTools = this.noEmbeddingsToolLimit > 0 ?
Math.min(this.noEmbeddingsToolLimit, candidateTools.length) :
@ -509,23 +509,23 @@ class ImprovedMicroTaskAIPipeline {
Math.min(this.noEmbeddingsConceptLimit, candidateConcepts.length) :
candidateConcepts.length;
toolsToSend = toolsWithFullData.slice(0, maxTools);
meToCoToSend = meToCoWithFullData.slice(0, maxTools);
conceptsToSend = conceptsWithFullData.slice(0, maxConcepts);
console.log(`[AI PIPELINE] Embeddings disabled: sending ${toolsToSend.length}/${candidateTools.length} tools (limit: ${this.noEmbeddingsToolLimit || 'none'})`);
console.log(`[AI PIPELINE] Embeddings disabled: sending ${meToCoToSend.length}/${candidateTools.length} meToCo (limit: ${this.noEmbeddingsToolLimit || 'none'})`);
}
const basePrompt = getPrompt('toolSelection', mode, userQuery, selectionMethod, this.maxSelectedItems);
const prompt = `${basePrompt}
VERFÜGBARE TOOLS (mit vollständigen Daten):
${JSON.stringify(toolsToSend, null, 2)}
${JSON.stringify(meToCoToSend, null, 2)}
VERFÜGBARE KONZEPTE (mit vollständigen Daten):
${JSON.stringify(conceptsToSend, null, 2)}`;
const estimatedTokens = this.estimateTokens(prompt);
console.log(`[AI PIPELINE] Method: ${selectionMethod}, Tools: ${toolsToSend.length}, Estimated tokens: ~${estimatedTokens}`);
console.log(`[AI PIPELINE] Method: ${selectionMethod}, Tools: ${meToCoToSend.length}, Estimated tokens: ~${estimatedTokens}`);
if (estimatedTokens > 35000) {
console.warn(`[AI PIPELINE] WARNING: Prompt tokens (${estimatedTokens}) may exceed model limits`);
@ -545,7 +545,7 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
{ error: 'Invalid JSON structure', response: response.slice(0, 200) },
10,
selectionStart,
{ aiModel: this.config.model, selectionMethod, tokensSent: estimatedTokens, toolsSent: toolsToSend.length }
{ aiModel: this.config.model, selectionMethod, tokensSent: estimatedTokens, meToCoSent: meToCoToSend.length }
);
}
@ -554,11 +554,11 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
const totalSelected = result.selectedTools.length + result.selectedConcepts.length;
if (totalSelected === 0) {
console.error('[AI PIPELINE] AI selection returned no tools');
console.error('[AI PIPELINE] AI selection returned no meToCo');
throw new Error('AI selection returned empty selection');
}
console.log(`[AI PIPELINE] AI selected: ${result.selectedTools.length} tools, ${result.selectedConcepts.length} concepts from ${toolsToSend.length} candidates`);
console.log(`[AI PIPELINE] AI selected: ${result.selectedTools.length} meToCo, ${result.selectedConcepts.length} concepts from ${meToCoToSend.length} candidates`);
const selectedTools = candidateTools.filter(tool => result.selectedTools.includes(tool.name));
const selectedConcepts = candidateConcepts.filter(concept => result.selectedConcepts.includes(concept.name));
@ -573,11 +573,11 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
selectedConceptCount: result.selectedConcepts.length,
reasoning: result.reasoning?.slice(0, 200) + '...',
finalToolNames: selectedTools.map(t => t.name),
selectionEfficiency: `${toolsToSend.length}${result.selectedTools.length}`
selectionEfficiency: `${meToCoToSend.length}${result.selectedTools.length}`
},
confidence,
selectionStart,
{ aiModel: this.config.model, selectionMethod, promptTokens: estimatedTokens, toolsSent: toolsToSend.length }
{ aiModel: this.config.model, selectionMethod, promptTokens: estimatedTokens, meToCoSent: meToCoToSend.length }
);
}
@ -710,7 +710,7 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
}
private async selectToolsForPhase(context: AnalysisContext, phase: any): Promise<MicroTaskResult> {
const phaseTools = context.filteredData.tools.filter((tool: any) =>
const phaseTools = context.filteredData.meToCo.filter((tool: any) =>
tool.phases && tool.phases.includes(phase.id)
);
@ -901,8 +901,8 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
console.log(`[AI PIPELINE] Starting ${mode} query processing with context continuity and audit trail`);
try {
const toolsData = await getCompressedToolsDataForAI();
const filteredData = await this.getIntelligentCandidates(userQuery, toolsData, mode);
const meToCoData = await getCompressedToolsDataForAI();
const filteredData = await this.getIntelligentCandidates(userQuery, meToCoData, mode);
const context: AnalysisContext = {
userQuery,
@ -917,11 +917,11 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
this.mergeTemporaryAuditEntries(context);
console.log(`[AI PIPELINE] Starting micro-tasks with ${filteredData.tools.length} tools visible`);
console.log(`[AI PIPELINE] Starting micro-tasks with ${filteredData.meToCo.length} meToCo visible`);
this.addAuditEntry(context, 'initialization', 'pipeline-start',
{ userQuery, mode, toolsDataLoaded: !!toolsData },
{ candidateTools: filteredData.tools.length, candidateConcepts: filteredData.concepts.length },
{ userQuery, mode, meToCoDataLoaded: !!meToCoData },
{ candidateTools: filteredData.meToCo.length, candidateConcepts: filteredData.concepts.length },
90,
startTime,
{ auditEnabled: this.auditConfig.enabled }
@ -946,14 +946,14 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
// Task 4: Tool Selection/Evaluation (mode-dependent)
if (mode === 'workflow') {
const phases = toolsData.phases || [];
const phases = meToCoData.phases || [];
for (const phase of phases) {
const toolSelectionResult = await this.selectToolsForPhase(context, phase);
if (toolSelectionResult.success) completedTasks++; else failedTasks++;
await this.delay(this.microTaskDelay);
}
} else {
const topTools = filteredData.tools.slice(0, 3);
const topTools = filteredData.meToCo.slice(0, 3);
for (let i = 0; i < topTools.length; i++) {
const evaluationResult = await this.evaluateSpecificTool(context, topTools[i], i + 1);
if (evaluationResult.success) completedTasks++; else failedTasks++;
@ -980,7 +980,7 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
const processingStats = {
embeddingsUsed: embeddingsService.isEnabled(),
candidatesFromEmbeddings: filteredData.tools.length,
candidatesFromEmbeddings: filteredData.meToCo.length,
finalSelectedItems: (context.selectedTools?.length || 0) +
(context.backgroundKnowledge?.length || 0),
processingTimeMs: Date.now() - startTime,
@ -990,7 +990,7 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
};
console.log(`[AI PIPELINE] Completed: ${completedTasks} tasks, Failed: ${failedTasks} tasks`);
console.log(`[AI PIPELINE] Unique tools selected: ${context.seenToolNames.size}`);
console.log(`[AI PIPELINE] Unique meToCo selected: ${context.seenToolNames.size}`);
console.log(`[AI PIPELINE] Audit trail entries: ${context.auditTrail.length}`);
return {
@ -1027,7 +1027,7 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
if (isWorkflow) {
return {
...base,
recommended_tools: context.selectedTools?.map(st => ({
recommended_meToCo: context.selectedTools?.map(st => ({
name: st.tool.name,
phase: st.phase,
priority: st.priority,
@ -1038,7 +1038,7 @@ ${JSON.stringify(conceptsToSend, null, 2)}`;
} else {
return {
...base,
recommended_tools: context.selectedTools?.map(st => ({
recommended_meToCo: context.selectedTools?.map(st => ({
name: st.tool.name,
rank: st.tool.evaluation?.rank || 1,
suitability_score: st.priority,

View File

@ -26,7 +26,7 @@ const ToolSchema = z.object({
});
const ToolsDataSchema = z.object({
tools: z.array(ToolSchema),
meToCo: z.array(ToolSchema),
domains: z.array(z.object({
id: z.string(),
name: z.string(),
@ -36,7 +36,7 @@ const ToolsDataSchema = z.object({
id: z.string(),
name: z.string(),
description: z.string().optional(),
typical_tools: z.array(z.string()).optional().default([]),
typical_meToCo: z.array(z.string()).optional().default([]),
key_activities: z.array(z.string()).optional().default([])
})),
'domain-agnostic-software': z.array(z.object({
@ -63,7 +63,7 @@ const ToolsDataSchema = z.object({
});
interface ToolsData {
tools: any[];
meToCo: any[];
domains: any[];
phases: any[];
'domain-agnostic-software': any[];
@ -72,7 +72,7 @@ interface ToolsData {
}
interface EnhancedCompressedToolsData {
tools: any[];
meToCo: any[];
concepts: any[];
domains: any[];
phases: any[];
@ -134,7 +134,7 @@ async function loadRawData(): Promise<ToolsData> {
cachedData.skill_levels = {
novice: "Minimal technical background required, guided interfaces",
beginner: "Basic IT knowledge, some command-line familiarity helpful",
intermediate: "Solid technical foundation, comfortable with various tools",
intermediate: "Solid technical foundation, comfortable with various meToCo",
advanced: "Extensive experience, deep technical understanding required",
expert: "Specialist knowledge, cutting-edge techniques and complex scenarios"
};
@ -159,11 +159,11 @@ export async function getToolsData(): Promise<ToolsData> {
const seed = getDailySeed();
const randomFn = seededRandom(seed);
const randomizedTools = shuffleArray(rawData.tools, randomFn);
const randomizedTools = shuffleArray(rawData.meToCo, randomFn);
cachedRandomizedData = {
...rawData,
tools: randomizedTools
meToCo: randomizedTools
};
lastRandomizationDate = today;
@ -177,7 +177,7 @@ export async function getCompressedToolsDataForAI(): Promise<EnhancedCompressedT
if (!cachedCompressedData) {
const data = await getToolsData();
const compressedTools = data.tools
const compressedTools = data.meToCo
.filter(tool => tool.type !== 'concept')
.map(tool => {
const { projectUrl, statusUrl, ...compressedTool } = tool;
@ -196,7 +196,7 @@ export async function getCompressedToolsDataForAI(): Promise<EnhancedCompressedT
};
});
const concepts = data.tools
const concepts = data.meToCo
.filter(tool => tool.type === 'concept')
.map(concept => {
const { projectUrl, statusUrl, platforms, accessType, license, ...compressedConcept } = concept;
@ -210,7 +210,7 @@ export async function getCompressedToolsDataForAI(): Promise<EnhancedCompressedT
});
cachedCompressedData = {
tools: compressedTools,
meToCo: compressedTools,
concepts: concepts,
domains: data.domains,
phases: data.phases,

View File

@ -73,8 +73,8 @@ class EmbeddingsService {
// Create data directory if it doesn't exist
await fs.mkdir(path.dirname(this.embeddingsPath), { recursive: true });
const toolsData = await getCompressedToolsDataForAI();
const currentDataHash = this.hashData(toolsData);
const meToCoData = await getCompressedToolsDataForAI();
const currentDataHash = this.hashData(meToCoData);
// Try to load existing embeddings
const existingEmbeddings = await this.loadEmbeddings();
@ -84,7 +84,7 @@ class EmbeddingsService {
this.embeddings = existingEmbeddings.embeddings;
} else {
console.log('[EMBEDDINGS] Generating new embeddings...');
await this.generateEmbeddings(toolsData, currentDataHash);
await this.generateEmbeddings(meToCoData, currentDataHash);
}
this.isInitialized = true;
@ -197,10 +197,10 @@ class EmbeddingsService {
throw new Error('Unknown embeddings API response format');
}
private async generateEmbeddings(toolsData: any, version: string): Promise<void> {
private async generateEmbeddings(meToCoData: any, version: string): Promise<void> {
const allItems = [
...toolsData.tools.map((tool: any) => ({ ...tool, type: 'tool' })),
...toolsData.concepts.map((concept: any) => ({ ...concept, type: 'concept' }))
...meToCoData.meToCo.map((tool: any) => ({ ...tool, type: 'tool' })),
...meToCoData.concepts.map((concept: any) => ({ ...concept, type: 'concept' }))
];
const contents = allItems.map(item => this.createContentString(item));

View File

@ -288,7 +288,7 @@ ${data.metadata.contact}
### For Maintainers
1. Copy the YAML above
2. Add to \`src/data/tools.yaml\` in the tools array
2. Add to \`src/data/tools.yaml\` in the meToCo array
3. Maintain alphabetical order
4. Close this issue when done

View File

@ -26,10 +26,10 @@ export function createToolSlug(toolName: string): string {
.replace(/^-|-$/g, ''); // Remove leading/trailing hyphens
}
export function findToolByIdentifier(tools: Tool[], identifier: string): Tool | undefined {
if (!identifier || !Array.isArray(tools)) return undefined;
export function findToolByIdentifier(meToCo: Tool[], identifier: string): Tool | undefined {
if (!identifier || !Array.isArray(meToCo)) return undefined;
return tools.find(tool =>
return meToCo.find(tool =>
tool.name === identifier ||
createToolSlug(tool.name) === identifier.toLowerCase()
);