From c01a73bbb71ba0ecdc411760c0bc375afff39f04 Mon Sep 17 00:00:00 2001 From: overcuriousity Date: Sat, 19 Jul 2025 23:15:57 +0200 Subject: [PATCH] make icons --- dfir_yaml_editor.html | 551 +++++++++++++++++++++----- public/fingerprint_scope_logo.svg | 60 --- src/components/AIQueryInterface.astro | 8 +- src/components/ToolCard.astro | 21 +- src/components/ToolMatrix.astro | 32 +- src/data/tools.yaml | 232 ++++++----- src/pages/index.astro | 7 +- src/utils/dataService.ts | 2 +- 8 files changed, 632 insertions(+), 281 deletions(-) delete mode 100644 public/fingerprint_scope_logo.svg diff --git a/dfir_yaml_editor.html b/dfir_yaml_editor.html index 7766f38..fe92f47 100644 --- a/dfir_yaml_editor.html +++ b/dfir_yaml_editor.html @@ -146,10 +146,26 @@ box-shadow: 0 8px 15px rgba(0,0,0,0.15); } + .tool-card.method { + border-left: 4px solid #9b59b6; + background: linear-gradient(135deg, #f8f9fa 0%, #f4f1ff 100%); + } + + .tool-card.software { + border-left: 4px solid #3498db; + } + .tool-card h3 { color: #2c3e50; margin-bottom: 10px; font-size: 1.3em; + display: flex; + align-items: center; + gap: 8px; + } + + .tool-icon { + font-size: 1.4em; } .tool-card p { @@ -169,6 +185,12 @@ margin: 2px; } + .tag.method-tag { + background: #e8e4ff; + color: #9b59b6; + font-weight: bold; + } + .tag.domain-agnostic { background: #e8f5e8; color: #27ae60; @@ -426,6 +448,42 @@ margin: 10px 0; border: 1px solid #c3e6cb; } + + .type-badge { + display: inline-block; + padding: 4px 8px; + border-radius: 12px; + font-size: 0.75em; + font-weight: bold; + text-transform: uppercase; + margin-left: 10px; + } + + .type-software { + background: #3498db; + color: white; + } + + .type-method { + background: #9b59b6; + color: white; + } + + .conditional-fields { + transition: opacity 0.3s ease; + } + + .conditional-fields.disabled { + opacity: 0.5; + pointer-events: none; + } + + .icon-input { + font-size: 1.5em; + text-align: center; + padding: 10px; + width: 80px; + } @@ -458,6 +516,14 @@
0
Total Tools
+
+
0
+
Software
+
+
+
0
+
Methods
+
0
Domains
@@ -496,7 +562,7 @@
- +
@@ -506,11 +572,31 @@

Add New Tool

-
+
+
+ + +
+
+ + +
+
+ +
+ + +
+ +
-
- -
- - -
- -
-
- - -
-
-
- - + +
+
+
+ + +
+
+ + +
+
+ + +
-
- - -
-
- - -
-
-
-
- - +
+
+ + +
+
+
+ + +
+
-
-
- -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
@@ -633,6 +720,8 @@
+ + 0 selected
@@ -643,11 +732,14 @@ + +
+
@@ -724,6 +816,25 @@ } } + function handleTypeChange() { + const type = document.getElementById('toolType').value; + const softwareFields = document.getElementById('softwareFields'); + + if (type === 'method') { + softwareFields.classList.add('disabled'); + // Clear software-specific fields for methods + document.getElementById('projectUrl').value = ''; + document.getElementById('license').value = ''; + document.getElementById('accessType').value = ''; + document.getElementById('statusUrl').value = ''; + document.getElementById('knowledgebase').checked = false; + // Clear platform checkboxes + document.querySelectorAll('#platformsCheckbox input').forEach(cb => cb.checked = false); + } else { + softwareFields.classList.remove('disabled'); + } + } + function showMessage(message, type = 'success') { const messageArea = document.getElementById('messageArea'); const className = type === 'error' ? 'error-message' : 'success-message'; @@ -757,24 +868,56 @@ } function loadSampleData() { - // This would load from your existing YAML data - // For brevity, I'll just show the structure try { const sampleData = { - tools: [], // Your existing tools - domains: [], // Your existing domains - phases: [], // Your existing phases - "domain-agnostic-software": [ + tools: [ { - id: "collaboration-general", - name: "Übergreifend & Kollaboration", - description: "Cross-cutting tools and collaboration platforms" + name: "Autopsy", + icon: "📱", + type: "software", + description: "The leading open-source digital forensics platform.", + domains: ["incident-response", "law-enforcement"], + phases: ["examination", "analysis"], + platforms: ["Windows", "Linux"], + skillLevel: "intermediate", + accessType: "download", + url: "https://www.autopsy.com/", + projectUrl: "", + license: "Apache 2.0", + knowledgebase: false, + tags: ["gui", "filesystem", "timeline-analysis", "carving"] }, { - id: "specific-os", - name: "Betriebssysteme", - description: "Operating Systems which focus on forensics" + name: "Live Memory Acquisition Procedure", + icon: "🧠", + type: "method", + description: "Standardized procedure for forensically sound memory acquisition.", + domains: ["incident-response", "law-enforcement"], + phases: ["data-collection"], + platforms: [], + skillLevel: "advanced", + accessType: null, + url: "https://www.nist.gov/publications/guide-integrating-forensic-techniques-incident-response", + projectUrl: null, + license: null, + knowledgebase: false, + tags: ["memory-acquisition", "volatile-evidence", "procedure"] } + ], + domains: [ + { id: "incident-response", name: "Incident Response & Breach Investigation" }, + { id: "law-enforcement", name: "Law Enforcement & Criminal Investigation" }, + { id: "malware-analysis", name: "Malware Analysis & Reverse Engineering" } + ], + phases: [ + { id: "data-collection", name: "Data Collection", description: "Imaging, Acquisition, Remote Collection Tools" }, + { id: "examination", name: "Examination", description: "Parsing, Extraction, Initial Analysis Tools" }, + { id: "analysis", name: "Analysis", description: "Deep Analysis, Correlation, Visualization Tools" }, + { id: "reporting", name: "Reporting", description: "Documentation, Visualization, Presentation Tools" } + ], + "domain-agnostic-software": [ + { id: "collaboration-general", name: "Collaboration & General", description: "Cross-cutting tools and collaboration platforms" }, + { id: "specific-os", name: "Operating Systems", description: "Operating Systems which focus on forensics" } ] }; yamlData = sampleData; @@ -808,6 +951,12 @@ const tools = yamlData.tools; document.getElementById('totalTools').textContent = tools.length; + + const softwareCount = tools.filter(tool => tool.type === 'software').length; + const methodCount = tools.filter(tool => tool.type === 'method').length; + document.getElementById('softwareCount').textContent = softwareCount; + document.getElementById('methodCount').textContent = methodCount; + document.getElementById('totalDomains').textContent = yamlData.domains ? yamlData.domains.length : 0; document.getElementById('totalPhases').textContent = yamlData.phases ? yamlData.phases.length : 0; document.getElementById('totalDomainAgnostic').textContent = yamlData['domain-agnostic-software'] ? yamlData['domain-agnostic-software'].length : 0; @@ -973,11 +1122,12 @@ function createToolCard(tool, index) { const card = document.createElement('div'); - card.className = 'tool-card'; + card.className = `tool-card ${tool.type || 'software'}`; const skillClass = `skill-${tool.skillLevel || 'intermediate'}`; const tags = (tool.tags || []).map(tag => `${tag}`).join(''); const knowledgebaseIndicator = tool.knowledgebase ? '📚 Knowledgebase' : ''; + const typeIndicator = `${tool.type || 'software'}`; // Add domain-agnostic indicators const domainAgnosticTags = (tool['domain-agnostic-software'] || []).map(cat => { @@ -986,7 +1136,11 @@ }).join(''); card.innerHTML = ` -

${tool.name}

+

+ ${tool.icon ? `${tool.icon}` : ''} + ${tool.name} + ${typeIndicator} +

${tool.skillLevel || 'intermediate'}
${knowledgebaseIndicator} @@ -1004,16 +1158,21 @@ function createBulkToolCard(tool, index) { const card = document.createElement('div'); - card.className = 'tool-card'; + card.className = `tool-card ${tool.type || 'software'}`; const skillClass = `skill-${tool.skillLevel || 'intermediate'}`; const isSelected = selectedTools.has(index); const knowledgebaseIndicator = tool.knowledgebase ? '📚 KB' : ''; + const typeIndicator = `${tool.type || 'software'}`; card.innerHTML = `
-

${tool.name}

+

+ ${tool.icon ? `${tool.icon}` : ''} + ${tool.name} +

+ ${typeIndicator} ${knowledgebaseIndicator}
${tool.skillLevel || 'intermediate'}
@@ -1046,6 +1205,7 @@ const searchableText = [ tool.name || '', tool.description || '', + tool.type || '', ...(tool.tags || []), ...(tool.domains || []), ...(tool.phases || []), @@ -1077,6 +1237,8 @@ // Populate form fields document.getElementById('toolName').value = tool.name || ''; + document.getElementById('toolType').value = tool.type || 'software'; + document.getElementById('toolIcon').value = tool.icon || ''; document.getElementById('description').value = tool.description || ''; document.getElementById('skillLevel').value = tool.skillLevel || ''; document.getElementById('url').value = tool.url || ''; @@ -1086,6 +1248,9 @@ document.getElementById('statusUrl').value = tool.statusUrl || ''; document.getElementById('knowledgebase').checked = tool.knowledgebase || false; + // Handle conditional fields + handleTypeChange(); + // Set checkboxes setCheckboxValues('#platformsCheckbox input', tool.platforms || []); setCheckboxValues('#domainsCheckbox input', tool.domains || []); @@ -1149,36 +1314,70 @@ yamlData.tools = []; } + const toolType = document.getElementById('toolType').value; const tool = { name: document.getElementById('toolName').value, + type: toolType, description: document.getElementById('description').value, domains: getCheckedValues('#domainsCheckbox input:checked'), phases: getCheckedValues('#phasesCheckbox input:checked'), - platforms: getCheckedValues('#platformsCheckbox input:checked'), - 'domain-agnostic-software': getCheckedValues('#domainAgnosticCheckbox input:checked'), skillLevel: document.getElementById('skillLevel').value, - accessType: document.getElementById('accessType').value, url: document.getElementById('url').value, - projectUrl: document.getElementById('projectUrl').value, - license: document.getElementById('license').value, - knowledgebase: document.getElementById('knowledgebase').checked, tags: getTags() }; + // Add icon if provided + const icon = document.getElementById('toolIcon').value.trim(); + if (icon) { + tool.icon = icon; + } + + // Add software-specific fields + if (toolType === 'software') { + tool.platforms = getCheckedValues('#platformsCheckbox input:checked'); + tool.accessType = document.getElementById('accessType').value; + tool.projectUrl = document.getElementById('projectUrl').value; + tool.license = document.getElementById('license').value; + tool.knowledgebase = document.getElementById('knowledgebase').checked; + + const statusUrl = document.getElementById('statusUrl').value; + if (statusUrl) { + tool.statusUrl = statusUrl; + } + } else { + // For methods, set appropriate defaults + tool.platforms = []; + tool.accessType = null; + tool.projectUrl = null; + tool.license = null; + tool.knowledgebase = false; + } + + // Add domain-agnostic software if selected + const domainAgnostic = getCheckedValues('#domainAgnosticCheckbox input:checked'); + if (domainAgnostic.length > 0) { + tool['domain-agnostic-software'] = domainAgnostic; + } else { + tool['domain-agnostic-software'] = null; + } + // Clean up empty arrays and null values Object.keys(tool).forEach(key => { if (Array.isArray(tool[key]) && tool[key].length === 0) { - delete tool[key]; + if (key === 'platforms' && toolType === 'method') { + tool[key] = []; // Keep empty array for methods + } else { + delete tool[key]; + } } else if (tool[key] === '' || tool[key] === null) { - delete tool[key]; + if ((key === 'accessType' || key === 'projectUrl' || key === 'license') && toolType === 'method') { + tool[key] = null; // Keep null for methods + } else { + delete tool[key]; + } } }); - const statusUrl = document.getElementById('statusUrl').value; - if (statusUrl) { - tool.statusUrl = statusUrl; - } - if (currentEditingIndex >= 0) { yamlData.tools[currentEditingIndex] = tool; showMessage('Tool updated successfully!'); @@ -1213,6 +1412,7 @@ document.getElementById('tagContainer').querySelectorAll('.removable-tag').forEach(tag => tag.remove()); document.getElementById('editorTitle').textContent = 'Add New Tool'; document.getElementById('deleteBtn').style.display = 'none'; + document.getElementById('softwareFields').classList.remove('disabled'); currentEditingIndex = -1; } @@ -1251,6 +1451,18 @@ } } + function selectByType(type) { + if (yamlData && yamlData.tools) { + yamlData.tools.forEach((tool, index) => { + if (tool.type === type) { + selectedTools.add(index); + } + }); + updateSelectionCount(); + renderBulkGrid(); + } + } + function clearSelection() { selectedTools.clear(); updateSelectionCount(); @@ -1261,6 +1473,111 @@ document.getElementById('selectionCount').textContent = `${selectedTools.size} selected`; } + function showIconSuggestions() { + const suggestions = `DFIR Tool Icons by Operational Mode: + +📦 Downloaded/Installed 🌐 Web Application ☁️ Cloud Service +🖥️ Operating System ⌨️ Command Line 📡 Server/Self-hosted +🔧 Hardware Tool 💰 Commercial ⚙️ Built-in/System +📱 Mobile Application 🔗 API/Library 📋 Method/Procedure + +🖲️ Remote Access 💻 Desktop GUI 🛠️ Utility/Helper +🏢 Enterprise Platform 🔓 Open Source 🎯 Specialized Tool +📊 Analysis Platform 🗄️ Database/Storage 🔄 Processing Engine + +Most common combinations: +📦 + 🔓 = Open Source Desktop Software +🌐 + ☁️ = Cloud Web Application +🖥️ + 🔓 = Forensic Live OS +⌨️ + 🔧 = Command Line Utility +📋 + 🎯 = Specialized Method + +Click any emoji to copy it, then paste into the icon field.`; + + alert(suggestions); + } + + function bulkUpdateIcons() { + if (selectedTools.size === 0) { + showMessage('No tools selected', 'error'); + return; + } + + // Show operational mode-focused icon suggestions + const suggestedIcons = ` +DFIR Tool Icons by Operational Mode: + +📦 Downloaded/Installed 🌐 Web Application ☁️ Cloud Service +🖥️ Operating System ⌨️ Command Line 📡 Server/Self-hosted +🔧 Hardware Tool 💰 Commercial ⚙️ Built-in/System +📱 Mobile Application 🔗 API/Library 📋 Method/Procedure + +🖲️ Remote Access 💻 Desktop GUI 🛠️ Utility/Helper +🏢 Enterprise Platform 🔓 Open Source 🎯 Specialized Tool +📊 Analysis Platform 🗄️ Database/Storage 🔄 Processing Engine + +Quick suggestions by access type: +• Downloaded Software: 📦 +• Web Applications: 🌐 +• Cloud Services: ☁️ +• Operating Systems: 🖥️ +• Command Line: ⌨️ +• Hardware: 🔧 +• Methods/Procedures: 📋`; + + const icon = prompt(`Enter emoji icon (single character) or leave empty to remove:\n\n${suggestedIcons}`); + + if (icon !== null) { // Allow empty string to remove icons + const trimmedIcon = icon.trim(); + if (trimmedIcon.length > 2) { + showMessage('Icon should be a single emoji character', 'error'); + return; + } + + selectedTools.forEach(index => { + if (trimmedIcon === '') { + delete yamlData.tools[index].icon; // Remove icon field entirely + } else { + yamlData.tools[index].icon = trimmedIcon; + } + }); + + const action = trimmedIcon === '' ? 'removed icons from' : 'updated icons for'; + showMessage(`Successfully ${action} ${selectedTools.size} tools`); + renderBulkGrid(); + renderToolsGrid(); // Update tools view if visible + } + } + + function bulkUpdateType() { + if (selectedTools.size === 0) { + showMessage('No tools selected', 'error'); + return; + } + + const type = prompt('Enter type (software/method):'); + if (type && ['software', 'method'].includes(type)) { + selectedTools.forEach(index => { + yamlData.tools[index].type = type; + + // Handle method-specific cleanup + if (type === 'method') { + yamlData.tools[index].platforms = []; + yamlData.tools[index].accessType = null; + yamlData.tools[index].projectUrl = null; + yamlData.tools[index].license = null; + yamlData.tools[index].knowledgebase = false; + if (yamlData.tools[index].statusUrl) { + delete yamlData.tools[index].statusUrl; + } + } + }); + showMessage(`Updated type for ${selectedTools.size} tools`); + updateStats(); + renderBulkGrid(); + } + } + function bulkUpdateSkillLevel() { if (selectedTools.size === 0) { showMessage('No tools selected', 'error'); @@ -1352,19 +1669,29 @@ return; } - const action = value ? 'set as knowledgebase' : 'remove knowledgebase flag from'; - if (!confirm(`Are you sure you want to ${action} ${selectedTools.size} selected tools?`)) { + // Only apply to software tools + const softwareTools = Array.from(selectedTools).filter(index => + yamlData.tools[index].type === 'software' + ); + + if (softwareTools.length === 0) { + showMessage('No software tools selected (knowledgebase only applies to software)', 'error'); return; } - selectedTools.forEach(index => { + const action = value ? 'set as knowledgebase' : 'remove knowledgebase flag from'; + if (!confirm(`Are you sure you want to ${action} ${softwareTools.length} selected software tools?`)) { + return; + } + + softwareTools.forEach(index => { if (yamlData.tools[index]) { yamlData.tools[index].knowledgebase = value; } }); const actionCompleted = value ? 'marked as knowledgebase' : 'removed knowledgebase flag from'; - showMessage(`Successfully ${actionCompleted} ${selectedTools.size} tools`); + showMessage(`Successfully ${actionCompleted} ${softwareTools.length} software tools`); updateStats(); renderBulkGrid(); @@ -1383,11 +1710,23 @@ selectedTools.forEach(index => { if (yamlData.tools[index]) { + const tool = yamlData.tools[index]; const arrayFields = ['tags', 'domains', 'phases', 'platforms', 'domain-agnostic-software']; + if (arrayFields.includes(fieldName)) { - yamlData.tools[index][fieldName] = []; + if (fieldName === 'platforms' && tool.type === 'method') { + tool[fieldName] = []; // Keep empty array for methods + } else { + tool[fieldName] = []; + } + } else if (fieldName === 'icon') { + delete tool.icon; // Remove icon field entirely } else { - yamlData.tools[index][fieldName] = ''; + if ((fieldName === 'projectUrl' || fieldName === 'accessType' || fieldName === 'license') && tool.type === 'method') { + tool[fieldName] = null; // Keep null for methods + } else { + tool[fieldName] = ''; + } } } }); @@ -1399,6 +1738,9 @@ } renderBulkGrid(); + if (fieldName === 'icon') { + renderToolsGrid(); // Update tools view to reflect icon changes + } } function bulkDelete() { @@ -1438,6 +1780,25 @@ 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`); + if (!tool.type) validationResults.push(`❌ Tool ${index + 1}: Missing type`); + if (tool.type && !['software', 'method'].includes(tool.type)) { + validationResults.push(`❌ Tool ${index + 1}: Invalid type (must be 'software' or 'method')`); + } + + // Software-specific validation + if (tool.type === 'software') { + if (!tool.platforms || tool.platforms.length === 0) { + validationResults.push(`❌ Tool ${index + 1}: Software must have platforms`); + } + if (!tool.license) validationResults.push(`❌ Tool ${index + 1}: Software should have license`); + } + + // Method-specific validation + if (tool.type === 'method') { + if (tool.platforms && tool.platforms.length > 0) { + validationResults.push(`⚠️ Tool ${index + 1}: Methods should not have platforms`); + } + } }); } diff --git a/public/fingerprint_scope_logo.svg b/public/fingerprint_scope_logo.svg deleted file mode 100644 index 35a8af9..0000000 --- a/public/fingerprint_scope_logo.svg +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/components/AIQueryInterface.astro b/src/components/AIQueryInterface.astro index d2e8f8f..fdab0f2 100644 --- a/src/components/AIQueryInterface.astro +++ b/src/components/AIQueryInterface.astro @@ -461,7 +461,10 @@ document.addEventListener('DOMContentLoaded', () => {
-

${tool.name}

+

+ ${tool.icon ? `${tool.icon}` : ''} + ${tool.name} +

${tool.recommendation.priority} @@ -474,9 +477,8 @@ document.addEventListener('DOMContentLoaded', () => {