forensic-pathways/dfir_yaml_editor.html
overcuriousity c01a73bbb7 make icons
2025-07-19 23:15:57 +02:00

1848 lines
72 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DFIR Tools YAML Editor</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-yaml/4.1.0/js-yaml.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
background: white;
border-radius: 15px;
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #2c3e50 0%, #3498db 100%);
color: white;
padding: 25px;
text-align: center;
}
.header h1 {
font-size: 2.2em;
margin-bottom: 10px;
}
.header p {
opacity: 0.9;
font-size: 1.1em;
}
.tabs {
background: #f8f9fa;
display: flex;
border-bottom: 2px solid #e9ecef;
}
.tab {
padding: 15px 25px;
cursor: pointer;
border-bottom: 3px solid transparent;
transition: all 0.3s ease;
font-weight: 500;
}
.tab:hover {
background: #e9ecef;
}
.tab.active {
background: white;
border-bottom-color: #3498db;
color: #3498db;
}
.tab-content {
display: none;
padding: 30px;
min-height: 600px;
}
.tab-content.active {
display: block;
}
.file-input-section {
background: #f8f9fa;
padding: 20px;
border-radius: 10px;
margin-bottom: 20px;
border: 2px dashed #dee2e6;
text-align: center;
}
.file-input-section input {
margin: 10px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
}
.btn {
background: linear-gradient(135deg, #3498db, #2980b9);
color: white;
border: none;
padding: 12px 24px;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: all 0.3s ease;
margin: 5px;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 15px rgba(52, 152, 219, 0.3);
}
.btn-danger {
background: linear-gradient(135deg, #e74c3c, #c0392b);
}
.btn-success {
background: linear-gradient(135deg, #27ae60, #229954);
}
.btn-warning {
background: linear-gradient(135deg, #f39c12, #e67e22);
}
.tools-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 20px;
margin-top: 20px;
}
.tool-card {
background: white;
border: 1px solid #e9ecef;
border-radius: 10px;
padding: 20px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
transition: all 0.3s ease;
}
.tool-card:hover {
transform: translateY(-5px);
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 {
color: #7f8c8d;
margin-bottom: 15px;
font-size: 0.9em;
line-height: 1.4;
}
.tag {
display: inline-block;
background: #ecf0f1;
color: #34495e;
padding: 4px 8px;
border-radius: 15px;
font-size: 0.8em;
margin: 2px;
}
.tag.method-tag {
background: #e8e4ff;
color: #9b59b6;
font-weight: bold;
}
.tag.domain-agnostic {
background: #e8f5e8;
color: #27ae60;
font-weight: bold;
}
.skill-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 20px;
font-size: 0.8em;
font-weight: bold;
margin: 5px 0;
}
.skill-novice { background: #d5f4e6; color: #158bc2; }
.skill-beginner { background: #d5f4e6; color: #27ae60; }
.skill-intermediate { background: #ffeaa7; color: #e17055; }
.skill-advanced { background: #fab1a0; color: #d63031; }
.skill-expert { background: #2c3e50; color: #ffffff; }
.form-section {
background: #f8f9fa;
padding: 25px;
border-radius: 10px;
margin-bottom: 20px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: #2c3e50;
}
.form-group input,
.form-group textarea,
.form-group select {
width: 100%;
padding: 12px;
border: 2px solid #e9ecef;
border-radius: 8px;
font-size: 14px;
transition: border-color 0.3s ease;
}
.form-group input:focus,
.form-group textarea:focus,
.form-group select:focus {
outline: none;
border-color: #3498db;
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1);
}
.checkbox-group {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 10px;
margin-top: 10px;
}
.checkbox-item {
display: flex;
align-items: center;
gap: 8px;
}
.search-bar {
width: 100%;
padding: 15px;
border: 2px solid #e9ecef;
border-radius: 10px;
font-size: 16px;
margin-bottom: 20px;
}
.bulk-operations {
background: #fff3cd;
border: 1px solid #ffeaa7;
border-radius: 10px;
padding: 20px;
margin-bottom: 20px;
}
.stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: white;
padding: 20px;
border-radius: 10px;
text-align: center;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.stat-number {
font-size: 2em;
font-weight: bold;
color: #3498db;
}
.stat-label {
color: #7f8c8d;
margin-top: 5px;
}
.hidden {
display: none !important;
}
.tag-input-container {
display: flex;
flex-wrap: wrap;
gap: 5px;
margin-bottom: 10px;
padding: 10px;
border: 2px solid #e9ecef;
border-radius: 8px;
min-height: 50px;
align-items: center;
}
.tag-input {
border: none;
outline: none;
padding: 5px;
flex: 1;
min-width: 100px;
}
.removable-tag {
background: #3498db;
color: white;
padding: 4px 8px;
border-radius: 15px;
font-size: 0.8em;
display: flex;
align-items: center;
gap: 5px;
}
.remove-tag {
cursor: pointer;
background: rgba(255,255,255,0.3);
border-radius: 50%;
width: 16px;
height: 16px;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
}
.export-section {
background: #e8f5e8;
border: 1px solid #c3e6c3;
border-radius: 10px;
padding: 20px;
margin-top: 20px;
}
.tag-stat {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
margin: 5px 0;
background: #f8f9fa;
border-radius: 8px;
border-left: 4px solid #3498db;
}
.tag-stat:hover {
background: #e9ecef;
cursor: pointer;
}
.tag-name {
font-weight: 500;
color: #2c3e50;
}
.tag-count {
background: #3498db;
color: white;
padding: 4px 8px;
border-radius: 12px;
font-size: 0.85em;
font-weight: bold;
}
.skill-bar {
display: flex;
align-items: center;
margin: 10px 0;
gap: 15px;
}
.skill-label {
width: 100px;
font-weight: 500;
text-transform: capitalize;
}
.skill-progress {
flex: 1;
height: 20px;
background: #e9ecef;
border-radius: 10px;
overflow: hidden;
position: relative;
}
.skill-fill {
height: 100%;
border-radius: 10px;
transition: width 0.3s ease;
}
.skill-fill.novice { background: linear-gradient(90deg, #77b7d3, #2e92cc); }
.skill-fill.beginner { background: linear-gradient(90deg, #27ae60, #2ecc71); }
.skill-fill.intermediate { background: linear-gradient(90deg, #f39c12, #e67e22); }
.skill-fill.advanced { background: linear-gradient(90deg, #e74c3c, #c0392b); }
.skill-fill.expert { background: linear-gradient(90deg, #2c3e50, #34495e); }
.skill-count {
min-width: 30px;
text-align: right;
font-weight: bold;
color: #2c3e50;
}
.error-message {
background: #f8d7da;
color: #721c24;
padding: 12px;
border-radius: 8px;
margin: 10px 0;
border: 1px solid #f5c6cb;
}
.success-message {
background: #d4edda;
color: #155724;
padding: 12px;
border-radius: 8px;
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;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🔧 DFIR Tools YAML Editor</h1>
<p>Comprehensive editor for Digital Forensics and Incident Response tools database</p>
</div>
<div class="tabs">
<div class="tab active" onclick="showTab('overview')">📊 Overview</div>
<div class="tab" onclick="showTab('tools')">🛠️ Tools</div>
<div class="tab" onclick="showTab('editor')">✏️ Editor</div>
<div class="tab" onclick="showTab('bulk')">📋 Bulk Edit</div>
<div class="tab" onclick="showTab('export')">💾 Export</div>
</div>
<!-- Overview Tab -->
<div id="overview" class="tab-content active">
<div class="file-input-section">
<h3>📁 Load YAML File</h3>
<input type="file" id="fileInput" accept=".yaml,.yml" />
<button class="btn" onclick="loadFile()">Load File</button>
<button class="btn btn-success" onclick="loadSampleData()">Load Sample Data</button>
</div>
<div id="statsSection" class="hidden">
<div class="stats">
<div class="stat-card">
<div class="stat-number" id="totalTools">0</div>
<div class="stat-label">Total Tools</div>
</div>
<div class="stat-card">
<div class="stat-number" id="softwareCount">0</div>
<div class="stat-label">Software</div>
</div>
<div class="stat-card">
<div class="stat-number" id="methodCount">0</div>
<div class="stat-label">Methods</div>
</div>
<div class="stat-card">
<div class="stat-number" id="totalDomains">0</div>
<div class="stat-label">Domains</div>
</div>
<div class="stat-card">
<div class="stat-number" id="totalPhases">0</div>
<div class="stat-label">Phases</div>
</div>
<div class="stat-card">
<div class="stat-number" id="totalDomainAgnostic">0</div>
<div class="stat-label">Domain-Agnostic</div>
</div>
<div class="stat-card">
<div class="stat-number" id="selfHostedCount">0</div>
<div class="stat-label">CC24-Server</div>
</div>
<div class="stat-card">
<div class="stat-number" id="knowledgebaseCount">0</div>
<div class="stat-label">Knowledgebase</div>
</div>
</div>
<div class="form-section">
<h3>🏷️ Tag Analytics</h3>
<div id="tagAnalytics" style="max-height: 400px; overflow-y: auto;">
<p>Loading tag statistics...</p>
</div>
</div>
<div class="form-section">
<h3>📊 Skill Level Distribution</h3>
<div id="skillDistribution"></div>
</div>
</div>
</div>
<!-- Tools Tab -->
<div id="tools" class="tab-content">
<input type="text" class="search-bar" id="searchBar" placeholder="🔍 Search tools by name, description, tags, domains, phases, or type..." onkeyup="filterTools()" />
<div class="tools-grid" id="toolsGrid"></div>
</div>
<!-- Editor Tab -->
<div id="editor" class="tab-content">
<div class="form-section">
<h3 id="editorTitle">Add New Tool</h3>
<div id="messageArea"></div>
<form id="toolForm">
<div style="display: grid; grid-template-columns: 1fr 1fr 100px; gap: 20px;">
<div class="form-group">
<label for="toolName">Tool Name *</label>
<input type="text" id="toolName" required />
</div>
<div class="form-group">
<label for="toolType">Type *</label>
<select id="toolType" required onchange="handleTypeChange()">
<option value="">Select Type</option>
<option value="software">Software</option>
<option value="method">Method</option>
</select>
</div>
<div class="form-group">
<label for="toolIcon">Icon <small style="color: #7f8c8d; cursor: pointer;" onclick="showIconSuggestions()" title="Click for suggestions">💡 suggestions</small></label>
<input type="text" id="toolIcon" class="icon-input" placeholder="🔧" maxlength="2" />
</div>
</div>
<div class="form-group">
<label for="description">Description *</label>
<textarea id="description" rows="3" required></textarea>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
<div class="form-group">
<label for="skillLevel">Skill Level *</label>
<select id="skillLevel" required>
<option value="">Select Level</option>
<option value="novice">Novice</option>
<option value="beginner">Beginner</option>
<option value="intermediate">Intermediate</option>
<option value="advanced">Advanced</option>
<option value="expert">Expert</option>
</select>
</div>
<div class="form-group">
<label for="url">URL</label>
<input type="url" id="url" />
</div>
</div>
<!-- Software-specific fields -->
<div id="softwareFields" class="conditional-fields">
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 20px;">
<div class="form-group">
<label for="projectUrl">Project URL</label>
<input type="url" id="projectUrl" />
</div>
<div class="form-group">
<label for="license">License</label>
<input type="text" id="license" />
</div>
<div class="form-group">
<label for="accessType">Access Type</label>
<select id="accessType">
<option value="">Select Type</option>
<option value="download">Download</option>
<option value="server-based">CC24-Server</option>
<option value="commercial">Commercial</option>
<option value="built-in">Built-in</option>
</select>
</div>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
<div class="form-group">
<label for="statusUrl">Status URL</label>
<input type="url" id="statusUrl" />
</div>
<div class="form-group">
<div class="checkbox-item">
<input type="checkbox" id="knowledgebase" />
<label for="knowledgebase">📚 Knowledgebase Tool</label>
</div>
</div>
</div>
<div class="form-group">
<label>Platforms</label>
<div class="checkbox-group" id="platformsCheckbox">
<div class="checkbox-item">
<input type="checkbox" id="platform-windows" value="Windows">
<label for="platform-windows">Windows</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="platform-linux" value="Linux">
<label for="platform-linux">Linux</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="platform-macos" value="macOS">
<label for="platform-macos">macOS</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="platform-web" value="Web">
<label for="platform-web">Web</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="platform-os" value="OS">
<label for="platform-os">Operating System</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="platform-hardware" value="Hardware">
<label for="platform-hardware">Hardware</label>
</div>
</div>
</div>
</div>
<div class="form-group">
<label>Domains</label>
<div class="checkbox-group" id="domainsCheckbox"></div>
</div>
<div class="form-group">
<label>Phases</label>
<div class="checkbox-group" id="phasesCheckbox"></div>
</div>
<div class="form-group">
<label>Domain-Agnostic Software Categories</label>
<div class="checkbox-group" id="domainAgnosticCheckbox"></div>
</div>
<div class="form-group">
<label for="tagsInput">Tags</label>
<div class="tag-input-container" id="tagContainer">
<input type="text" class="tag-input" id="tagsInput" placeholder="Type and press Enter to add tags..." onkeypress="addTagOnEnter(event)" />
</div>
</div>
<div style="text-align: center; margin-top: 30px;">
<button type="button" class="btn btn-success" onclick="saveTool()">💾 Save Tool</button>
<button type="button" class="btn btn-warning" onclick="clearForm()">🗑️ Clear Form</button>
<button type="button" class="btn btn-danger" id="deleteBtn" onclick="deleteTool()" style="display: none;">🗑️ Delete Tool</button>
</div>
</form>
</div>
</div>
<!-- Bulk Edit Tab -->
<div id="bulk" class="tab-content">
<div class="bulk-operations">
<h3>🔄 Bulk Operations</h3>
<p>Select multiple tools to perform bulk operations</p>
<div style="margin: 20px 0;">
<button class="btn" onclick="selectAllTools()">Select All</button>
<button class="btn" onclick="selectByType('software')">Select Software</button>
<button class="btn" onclick="selectByType('method')">Select Methods</button>
<button class="btn" onclick="clearSelection()">Clear Selection</button>
<span id="selectionCount" style="margin-left: 20px; font-weight: bold;">0 selected</span>
</div>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-top: 20px;">
<button class="btn btn-warning" onclick="bulkUpdateSkillLevel()">Update Skill Level</button>
<button class="btn btn-warning" onclick="bulkUpdateDomains()">Update Domains</button>
<button class="btn btn-warning" onclick="bulkUpdatePhases()">Update Phases</button>
<button class="btn btn-warning" onclick="bulkUpdateDomainAgnostic()">Update Domain-Agnostic</button>
<button class="btn btn-warning" onclick="bulkUpdateTags()">Update Tags</button>
<button class="btn btn-warning" onclick="bulkUpdateType()">Update Type</button>
<button class="btn btn-warning" onclick="bulkUpdateIcons()">🎨 Update Icons</button>
</div>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-top: 15px;">
<button class="btn" onclick="bulkSetKnowledgebase(true)">📚 Set as Knowledgebase</button>
<button class="btn" onclick="bulkSetKnowledgebase(false)">📖 Remove Knowledgebase</button>
<button class="btn" onclick="bulkClearField('icon')">🗑️ Clear Icons</button>
<button class="btn" onclick="bulkClearField('tags')">🗑️ Clear All Tags</button>
<button class="btn" onclick="bulkClearField('domains')">🗑️ Clear All Domains</button>
</div>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-top: 15px;">
<button class="btn" onclick="bulkClearField('phases')">🗑️ Clear All Phases</button>
<button class="btn" onclick="bulkClearField('domain-agnostic-software')">🗑️ Clear Domain-Agnostic</button>
<button class="btn" onclick="bulkClearField('platforms')">🗑️ Clear All Platforms</button>
<button class="btn" onclick="bulkClearField('url')">🗑️ Clear All URLs</button>
</div>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-top: 15px;">
<button class="btn" onclick="bulkClearField('projectUrl')">🗑️ Clear Project URLs</button>
<button class="btn" onclick="bulkClearField('statusUrl')">🗑️ Clear Status URLs</button>
<button class="btn btn-danger" onclick="bulkDelete()">🗑️ Delete Selected</button>
</div>
</div>
<div class="tools-grid" id="bulkToolsGrid"></div>
</div>
<!-- Export Tab -->
<div id="export" class="tab-content">
<div class="export-section">
<h3>💾 Export Options</h3>
<p>Download your edited YAML file</p>
<div style="margin: 20px 0;">
<button class="btn btn-success" onclick="exportYAML()">📥 Download YAML</button>
<button class="btn" onclick="previewYAML()">👁️ Preview YAML</button>
<button class="btn btn-warning" onclick="validateYAML()">✅ Validate Structure</button>
</div>
</div>
<div id="yamlPreview" class="form-section hidden">
<h3>YAML Preview</h3>
<textarea readonly style="width: 100%; height: 400px; font-family: monospace; font-size: 12px;" id="yamlPreviewText"></textarea>
</div>
<div id="validationResults" class="form-section hidden">
<h3>Validation Results</h3>
<div id="validationContent"></div>
</div>
</div>
</div>
<script>
let yamlData = null;
let currentEditingIndex = -1;
let selectedTools = new Set();
function showTab(tabName) {
// Hide all tab contents
document.querySelectorAll('.tab-content').forEach(content => {
content.classList.remove('active');
});
// Remove active class from all tabs
document.querySelectorAll('.tab').forEach(tab => {
tab.classList.remove('active');
});
// Show selected tab content
document.getElementById(tabName).classList.add('active');
// Add active class to clicked tab
event.target.classList.add('active');
// Refresh content based on tab
if (tabName === 'tools') {
renderToolsGrid();
} else if (tabName === 'bulk') {
renderBulkGrid();
}
}
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';
messageArea.innerHTML = `<div class="${className}">${message}</div>`;
setTimeout(() => {
messageArea.innerHTML = '';
}, 5000);
}
function loadFile() {
const fileInput = document.getElementById('fileInput');
const file = fileInput.files[0];
if (!file) {
showMessage('Please select a file', 'error');
return;
}
const reader = new FileReader();
reader.onload = function(e) {
try {
yamlData = jsyaml.load(e.target.result);
console.log('Loaded YAML data:', yamlData);
updateUI();
showMessage('File loaded successfully!');
} catch (error) {
showMessage('Error parsing YAML: ' + error.message, 'error');
}
};
reader.readAsText(file);
}
function loadSampleData() {
try {
const sampleData = {
tools: [
{
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"]
},
{
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;
updateUI();
showMessage('Sample data loaded successfully!');
} catch (error) {
showMessage('Error loading sample data: ' + error.message, 'error');
}
}
function updateUI() {
if (!yamlData) return;
// Show stats section
document.getElementById('statsSection').classList.remove('hidden');
// Update statistics
updateStats();
// Update checkboxes
updateDomainCheckboxes();
updatePhaseCheckboxes();
updateDomainAgnosticCheckboxes();
// Render tools grid
renderToolsGrid();
}
function updateStats() {
if (!yamlData || !yamlData.tools) return;
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;
const selfHosted = tools.filter(tool => tool.accessType === 'server-based').length;
document.getElementById('selfHostedCount').textContent = selfHosted;
const knowledgebaseTools = tools.filter(tool => tool.knowledgebase === true).length;
document.getElementById('knowledgebaseCount').textContent = knowledgebaseTools;
// Update analytics
updateTagAnalytics();
updateSkillDistribution();
}
function updateTagAnalytics() {
const tagCounts = {};
if (yamlData && yamlData.tools) {
yamlData.tools.forEach(tool => {
if (tool.tags && Array.isArray(tool.tags)) {
tool.tags.forEach(tag => {
tagCounts[tag] = (tagCounts[tag] || 0) + 1;
});
}
});
}
const container = document.getElementById('tagAnalytics');
if (Object.keys(tagCounts).length === 0) {
container.innerHTML = '<p style="color: #7f8c8d; font-style: italic;">No tags found</p>';
return;
}
const sortedTags = Object.entries(tagCounts).sort((a, b) => b[1] - a[1]);
container.innerHTML = sortedTags.map(([tag, count]) => `
<div class="tag-stat" onclick="searchForTag('${tag}')">
<span class="tag-name">${tag}</span>
<span class="tag-count">${count}</span>
</div>
`).join('');
}
function updateSkillDistribution() {
const skillCounts = { novice: 0, beginner: 0, intermediate: 0, advanced: 0, expert: 0 };
if (yamlData && yamlData.tools) {
yamlData.tools.forEach(tool => {
const skill = tool.skillLevel || 'intermediate';
if (skillCounts.hasOwnProperty(skill)) {
skillCounts[skill]++;
}
});
}
const total = Object.values(skillCounts).reduce((sum, count) => sum + count, 0);
const container = document.getElementById('skillDistribution');
if (total === 0) {
container.innerHTML = '<p style="color: #7f8c8d; font-style: italic;">No skill data available</p>';
return;
}
container.innerHTML = Object.entries(skillCounts).map(([skill, count]) => {
const percentage = total > 0 ? (count / total) * 100 : 0;
return `
<div class="skill-bar">
<div class="skill-label">${skill}</div>
<div class="skill-progress">
<div class="skill-fill ${skill}" style="width: ${percentage}%"></div>
</div>
<div class="skill-count">${count}</div>
</div>
`;
}).join('');
}
function searchForTag(tag) {
showTab('tools');
document.querySelector('[onclick="showTab(\'tools\')"]').classList.add('active');
document.getElementById('searchBar').value = tag;
filterTools();
}
function updateDomainCheckboxes() {
const container = document.getElementById('domainsCheckbox');
container.innerHTML = '';
if (yamlData.domains) {
yamlData.domains.forEach(domain => {
const div = document.createElement('div');
div.className = 'checkbox-item';
div.innerHTML = `
<input type="checkbox" id="domain-${domain.id}" value="${domain.id}">
<label for="domain-${domain.id}">${domain.name}</label>
`;
container.appendChild(div);
});
}
}
function updatePhaseCheckboxes() {
const container = document.getElementById('phasesCheckbox');
container.innerHTML = '';
if (yamlData.phases) {
yamlData.phases.forEach(phase => {
const div = document.createElement('div');
div.className = 'checkbox-item';
div.innerHTML = `
<input type="checkbox" id="phase-${phase.id}" value="${phase.id}">
<label for="phase-${phase.id}">${phase.name}</label>
`;
container.appendChild(div);
});
}
}
function updateDomainAgnosticCheckboxes() {
const container = document.getElementById('domainAgnosticCheckbox');
container.innerHTML = '';
if (yamlData['domain-agnostic-software']) {
yamlData['domain-agnostic-software'].forEach(category => {
const div = document.createElement('div');
div.className = 'checkbox-item';
div.innerHTML = `
<input type="checkbox" id="domain-agnostic-${category.id}" value="${category.id}">
<label for="domain-agnostic-${category.id}">${category.name}</label>
`;
container.appendChild(div);
});
}
}
function renderToolsGrid() {
const container = document.getElementById('toolsGrid');
container.innerHTML = '';
if (!yamlData || !yamlData.tools) return;
yamlData.tools.forEach((tool, index) => {
const card = createToolCard(tool, index);
container.appendChild(card);
});
}
function renderBulkGrid() {
const container = document.getElementById('bulkToolsGrid');
container.innerHTML = '';
if (!yamlData || !yamlData.tools) return;
yamlData.tools.forEach((tool, index) => {
const card = createBulkToolCard(tool, index);
container.appendChild(card);
});
updateSelectionCount();
}
function createToolCard(tool, index) {
const card = document.createElement('div');
card.className = `tool-card ${tool.type || 'software'}`;
const skillClass = `skill-${tool.skillLevel || 'intermediate'}`;
const tags = (tool.tags || []).map(tag => `<span class="tag">${tag}</span>`).join('');
const knowledgebaseIndicator = tool.knowledgebase ? '<span class="tag" style="background: #e8f5e8; color: #27ae60; font-weight: bold;">📚 Knowledgebase</span>' : '';
const typeIndicator = `<span class="type-badge type-${tool.type || 'software'}">${tool.type || 'software'}</span>`;
// Add domain-agnostic indicators
const domainAgnosticTags = (tool['domain-agnostic-software'] || []).map(cat => {
const categoryName = getDomainAgnosticName(cat);
return `<span class="tag domain-agnostic">🔧 ${categoryName}</span>`;
}).join('');
card.innerHTML = `
<h3>
${tool.icon ? `<span class="tool-icon">${tool.icon}</span>` : ''}
${tool.name}
${typeIndicator}
</h3>
<div style="margin: 5px 0;">
<div class="skill-badge ${skillClass}">${tool.skillLevel || 'intermediate'}</div>
${knowledgebaseIndicator}
</div>
<p>${tool.description}</p>
<div style="margin: 10px 0;">${tags}${domainAgnosticTags}</div>
<div style="margin-top: 15px;">
<button class="btn" onclick="editTool(${index})">✏️ Edit</button>
<button class="btn btn-danger" onclick="confirmDelete(${index})">🗑️ Delete</button>
</div>
`;
return card;
}
function createBulkToolCard(tool, index) {
const card = document.createElement('div');
card.className = `tool-card ${tool.type || 'software'}`;
const skillClass = `skill-${tool.skillLevel || 'intermediate'}`;
const isSelected = selectedTools.has(index);
const knowledgebaseIndicator = tool.knowledgebase ? '<span class="tag" style="background: #e8f5e8; color: #27ae60; font-weight: bold; margin-left: 10px;">📚 KB</span>' : '';
const typeIndicator = `<span class="type-badge type-${tool.type || 'software'}" style="margin-left: 10px;">${tool.type || 'software'}</span>`;
card.innerHTML = `
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 10px;">
<input type="checkbox" ${isSelected ? 'checked' : ''} onchange="toggleToolSelection(${index})" />
<h3 style="margin: 0; display: flex; align-items: center; gap: 8px;">
${tool.icon ? `<span class="tool-icon">${tool.icon}</span>` : ''}
${tool.name}
</h3>
${typeIndicator}
${knowledgebaseIndicator}
</div>
<div class="skill-badge ${skillClass}">${tool.skillLevel || 'intermediate'}</div>
<p>${tool.description}</p>
`;
if (isSelected) {
card.style.border = '2px solid #3498db';
card.style.backgroundColor = '#f8f9ff';
}
return card;
}
function getDomainAgnosticName(id) {
if (!yamlData['domain-agnostic-software']) return id;
const category = yamlData['domain-agnostic-software'].find(cat => cat.id === id);
return category ? category.name : id;
}
function filterTools() {
const searchTerm = document.getElementById('searchBar').value.toLowerCase();
const cards = document.querySelectorAll('#toolsGrid .tool-card');
if (!yamlData || !yamlData.tools) return;
cards.forEach((card, index) => {
const tool = yamlData.tools[index];
const searchableText = [
tool.name || '',
tool.description || '',
tool.type || '',
...(tool.tags || []),
...(tool.domains || []),
...(tool.phases || []),
...(tool.platforms || []),
...(tool['domain-agnostic-software'] || []),
tool.skillLevel || '',
tool.license || '',
tool.accessType || '',
tool.knowledgebase ? 'knowledgebase' : ''
].join(' ').toLowerCase();
if (searchableText.includes(searchTerm)) {
card.style.display = '';
} else {
card.style.display = 'none';
}
});
}
function editTool(index) {
currentEditingIndex = index;
const tool = yamlData.tools[index];
showTab('editor');
document.querySelector('[onclick="showTab(\'editor\')"]').classList.add('active');
document.getElementById('editorTitle').textContent = `Edit Tool: ${tool.name}`;
document.getElementById('deleteBtn').style.display = 'inline-block';
// 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 || '';
document.getElementById('projectUrl').value = tool.projectUrl || '';
document.getElementById('license').value = tool.license || '';
document.getElementById('accessType').value = tool.accessType || '';
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 || []);
setCheckboxValues('#phasesCheckbox input', tool.phases || []);
setCheckboxValues('#domainAgnosticCheckbox input', tool['domain-agnostic-software'] || []);
populateTags(tool.tags || []);
}
function setCheckboxValues(selector, values) {
document.querySelectorAll(selector).forEach(checkbox => {
checkbox.checked = values.includes(checkbox.value);
});
}
function populateTags(tags) {
const container = document.getElementById('tagContainer');
container.querySelectorAll('.removable-tag').forEach(tag => tag.remove());
tags.forEach(tag => {
addTag(tag);
});
}
function addTagOnEnter(event) {
if (event.key === 'Enter') {
event.preventDefault();
const input = event.target;
const tag = input.value.trim();
if (tag) {
addTag(tag);
input.value = '';
}
}
}
function addTag(tagText) {
const container = document.getElementById('tagContainer');
const input = container.querySelector('.tag-input');
const tagElement = document.createElement('span');
tagElement.className = 'removable-tag';
tagElement.innerHTML = `
${tagText}
<span class="remove-tag" onclick="removeTag(this)">×</span>
`;
container.insertBefore(tagElement, input);
}
function removeTag(element) {
element.parentElement.remove();
}
function saveTool() {
try {
if (!yamlData) {
yamlData = { tools: [], domains: [], phases: [], 'domain-agnostic-software': [] };
}
if (!yamlData.tools) {
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'),
skillLevel: document.getElementById('skillLevel').value,
url: document.getElementById('url').value,
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) {
if (key === 'platforms' && toolType === 'method') {
tool[key] = []; // Keep empty array for methods
} else {
delete tool[key];
}
} else if (tool[key] === '' || tool[key] === null) {
if ((key === 'accessType' || key === 'projectUrl' || key === 'license') && toolType === 'method') {
tool[key] = null; // Keep null for methods
} else {
delete tool[key];
}
}
});
if (currentEditingIndex >= 0) {
yamlData.tools[currentEditingIndex] = tool;
showMessage('Tool updated successfully!');
} else {
yamlData.tools.push(tool);
showMessage('Tool added successfully!');
}
clearForm();
updateStats();
renderToolsGrid();
} catch (error) {
showMessage('Error saving tool: ' + error.message, 'error');
}
}
function getCheckedValues(selector) {
return Array.from(document.querySelectorAll(selector)).map(checkbox => checkbox.value);
}
function getTags() {
const tags = [];
document.querySelectorAll('#tagContainer .removable-tag').forEach(tagElement => {
const text = tagElement.textContent.replace('×', '').trim();
if (text) tags.push(text);
});
return tags;
}
function clearForm() {
document.getElementById('toolForm').reset();
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;
}
function confirmDelete(index) {
if (confirm(`Are you sure you want to delete "${yamlData.tools[index].name}"?`)) {
deleteTool(index);
}
}
function deleteTool(index = currentEditingIndex) {
if (index >= 0 && yamlData && yamlData.tools) {
yamlData.tools.splice(index, 1);
clearForm();
updateStats();
renderToolsGrid();
showMessage('Tool deleted successfully!');
}
}
// Bulk operations
function toggleToolSelection(index) {
if (selectedTools.has(index)) {
selectedTools.delete(index);
} else {
selectedTools.add(index);
}
updateSelectionCount();
renderBulkGrid();
}
function selectAllTools() {
if (yamlData && yamlData.tools) {
yamlData.tools.forEach((_, index) => selectedTools.add(index));
updateSelectionCount();
renderBulkGrid();
}
}
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();
renderBulkGrid();
}
function updateSelectionCount() {
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');
return;
}
const skillLevel = prompt('Enter skill level (novice/beginner/intermediate/advanced/expert):');
if (skillLevel && ['novice','beginner', 'intermediate', 'advanced', 'expert'].includes(skillLevel)) {
selectedTools.forEach(index => {
yamlData.tools[index].skillLevel = skillLevel;
});
showMessage(`Updated skill level for ${selectedTools.size} tools`);
renderBulkGrid();
}
}
function bulkUpdateDomains() {
if (selectedTools.size === 0) {
showMessage('No tools selected', 'error');
return;
}
const domains = prompt('Enter domains (comma-separated):');
if (domains) {
const domainList = domains.split(',').map(d => d.trim()).filter(d => d);
selectedTools.forEach(index => {
yamlData.tools[index].domains = domainList;
});
showMessage(`Updated domains for ${selectedTools.size} tools`);
renderBulkGrid();
}
}
function bulkUpdatePhases() {
if (selectedTools.size === 0) {
showMessage('No tools selected', 'error');
return;
}
const phases = prompt('Enter phases (comma-separated):');
if (phases) {
const phaseList = phases.split(',').map(p => p.trim()).filter(p => p);
selectedTools.forEach(index => {
yamlData.tools[index].phases = phaseList;
});
showMessage(`Updated phases for ${selectedTools.size} tools`);
renderBulkGrid();
}
}
function bulkUpdateDomainAgnostic() {
if (selectedTools.size === 0) {
showMessage('No tools selected', 'error');
return;
}
const categories = prompt('Enter domain-agnostic categories (comma-separated):');
if (categories) {
const categoryList = categories.split(',').map(c => c.trim()).filter(c => c);
selectedTools.forEach(index => {
yamlData.tools[index]['domain-agnostic-software'] = categoryList;
});
showMessage(`Updated domain-agnostic categories for ${selectedTools.size} tools`);
renderBulkGrid();
}
}
function bulkUpdateTags() {
if (selectedTools.size === 0) {
showMessage('No tools selected', 'error');
return;
}
const tags = prompt('Enter tags (comma-separated):');
if (tags) {
const tagList = tags.split(',').map(t => t.trim()).filter(t => t);
selectedTools.forEach(index => {
yamlData.tools[index].tags = tagList;
});
showMessage(`Updated tags for ${selectedTools.size} tools`);
updateStats();
renderBulkGrid();
}
}
function bulkSetKnowledgebase(value) {
if (selectedTools.size === 0) {
showMessage('No tools selected', 'error');
return;
}
// 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;
}
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} ${softwareTools.length} software tools`);
updateStats();
renderBulkGrid();
}
function bulkClearField(fieldName) {
if (selectedTools.size === 0) {
showMessage('No tools selected', 'error');
return;
}
const fieldDisplayName = fieldName.charAt(0).toUpperCase() + fieldName.slice(1).replace('-', ' ');
if (!confirm(`Are you sure you want to clear ${fieldDisplayName} for ${selectedTools.size} selected tools?`)) {
return;
}
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)) {
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 {
if ((fieldName === 'projectUrl' || fieldName === 'accessType' || fieldName === 'license') && tool.type === 'method') {
tool[fieldName] = null; // Keep null for methods
} else {
tool[fieldName] = '';
}
}
}
});
showMessage(`Cleared ${fieldDisplayName} for ${selectedTools.size} tools`);
if (fieldName === 'tags') {
updateStats();
}
renderBulkGrid();
if (fieldName === 'icon') {
renderToolsGrid(); // Update tools view to reflect icon changes
}
}
function bulkDelete() {
if (selectedTools.size === 0) {
showMessage('No tools selected', 'error');
return;
}
if (confirm(`Are you sure you want to delete ${selectedTools.size} tools?`)) {
const indices = Array.from(selectedTools).sort((a, b) => b - a);
indices.forEach(index => {
yamlData.tools.splice(index, 1);
});
selectedTools.clear();
updateStats();
renderBulkGrid();
showMessage('Tools deleted successfully!');
}
}
function validateYAML() {
if (!yamlData) {
showMessage('No data to validate', 'error');
return;
}
const validationResults = [];
// Check required sections
if (!yamlData.tools) validationResults.push('❌ Missing tools section');
if (!yamlData.domains) validationResults.push('❌ Missing domains section');
if (!yamlData.phases) validationResults.push('❌ Missing phases section');
// Validate tools
if (yamlData.tools) {
yamlData.tools.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`);
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`);
}
}
});
}
const container = document.getElementById('validationContent');
if (validationResults.length === 0) {
container.innerHTML = '<div class="success-message">✅ YAML structure is valid!</div>';
} else {
container.innerHTML = '<div class="error-message">' + validationResults.join('<br>') + '</div>';
}
document.getElementById('validationResults').classList.remove('hidden');
}
function previewYAML() {
if (!yamlData) {
showMessage('No data to preview', 'error');
return;
}
const yamlString = jsyaml.dump(yamlData, { indent: 2 });
document.getElementById('yamlPreviewText').value = yamlString;
document.getElementById('yamlPreview').classList.remove('hidden');
}
function exportYAML() {
if (!yamlData) {
showMessage('No data to export', 'error');
return;
}
const yamlString = jsyaml.dump(yamlData, { indent: 2 });
const blob = new Blob([yamlString], { type: 'text/yaml' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'dfir-tools.yaml';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
showMessage('YAML file exported successfully!');
}
</script>
</body>
</html>