forensic-pathways/dfir_yaml_editor.html
2025-07-20 22:59:08 +02:00

1654 lines
61 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-weight: 500;
margin: 5px;
transition: all 0.3s ease;
text-decoration: none;
display: inline-block;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(52, 152, 219, 0.3);
}
.btn-secondary {
background: linear-gradient(135deg, #95a5a6, #7f8c8d);
}
.btn-danger {
background: linear-gradient(135deg, #e74c3c, #c0392b);
}
.btn-success {
background: linear-gradient(135deg, #27ae60, #229954);
}
.btn-warning {
background: linear-gradient(135deg, #f39c12, #e67e22);
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: linear-gradient(135deg, #3498db, #2980b9);
color: white;
padding: 20px;
border-radius: 10px;
text-align: center;
}
.stat-card h3 {
font-size: 2em;
margin-bottom: 5px;
}
.stat-card p {
opacity: 0.9;
}
.tools-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
margin-top: 20px;
}
.tool-card {
background: white;
border: 1px solid #e9ecef;
border-radius: 10px;
padding: 20px;
transition: all 0.3s ease;
border-left: 4px solid #3498db;
}
.tool-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.tool-card.software {
border-left-color: #3498db;
}
.tool-card.method {
border-left-color: #9b59b6;
}
.tool-card.concept {
border-left-color: #e67e22;
}
.tool-card h3 {
color: #2c3e50;
margin-bottom: 10px;
display: flex;
align-items: center;
gap: 10px;
}
.tool-card p {
color: #666;
margin-bottom: 15px;
line-height: 1.5;
}
.tool-actions {
display: flex;
gap: 10px;
margin-top: 15px;
}
.tool-actions button {
flex: 1;
padding: 8px 12px;
font-size: 0.9em;
}
.skill-level {
display: inline-block;
padding: 4px 8px;
border-radius: 15px;
font-size: 0.8em;
font-weight: bold;
color: white;
}
.skill-novice { background: #77b7d3; }
.skill-beginner { background: #27ae60; }
.skill-intermediate { background: #f39c12; }
.skill-advanced { background: #e74c3c; }
.skill-expert { background: #2c3e50; }
.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;
}
.type-concept {
background: #e67e22;
color: white;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 500;
color: #2c3e50;
}
.form-group input,
.form-group select,
.form-group textarea {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 14px;
}
.form-group textarea {
resize: vertical;
min-height: 100px;
}
.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;
}
.tags-input {
display: flex;
flex-wrap: wrap;
gap: 5px;
min-height: 40px;
padding: 5px;
border: 1px solid #ddd;
border-radius: 5px;
background: white;
}
.tag {
background: #e9ecef;
padding: 4px 8px;
border-radius: 15px;
font-size: 0.85em;
display: flex;
align-items: center;
gap: 5px;
}
.tag.domain-agnostic {
background: #d1ecf1;
color: #0c5460;
}
.tag.related-concept {
background: #fff3cd;
color: #856404;
}
.tag-remove {
cursor: pointer;
font-weight: bold;
color: #666;
}
.tag-remove:hover {
color: #e74c3c;
}
.conditional-fields {
transition: opacity 0.3s ease;
}
.conditional-fields.disabled {
opacity: 0.5;
pointer-events: none;
}
.hidden {
display: none !important;
}
.message {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
}
.message.success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.message.error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.bulk-actions {
background: #f8f9fa;
padding: 20px;
border-radius: 10px;
margin-bottom: 20px;
}
.bulk-actions h3 {
margin-bottom: 15px;
color: #2c3e50;
}
.selection-info {
margin-bottom: 15px;
padding: 10px;
background: #e9ecef;
border-radius: 5px;
font-weight: 500;
}
.filter-section {
background: #f8f9fa;
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
}
.filter-row {
display: flex;
gap: 15px;
align-items: center;
flex-wrap: wrap;
}
.filter-row select {
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
.knowledge-generator {
background: #fff3cd;
border: 1px solid #ffeaa7;
border-radius: 8px;
padding: 20px;
margin-top: 20px;
}
.knowledge-generator h3 {
color: #856404;
margin-bottom: 15px;
}
.markdown-preview {
background: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 4px;
padding: 15px;
font-family: 'Courier New', monospace;
font-size: 0.9em;
white-space: pre-wrap;
max-height: 400px;
overflow-y: auto;
}
@media (max-width: 768px) {
.tabs {
flex-direction: column;
}
.stats-grid {
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.tools-grid {
grid-template-columns: 1fr;
}
}
</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, 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('editor')">✏️ Editor</div>
<div class="tab" onclick="showTab('bulk')">📋 Bulk Edit</div>
<div class="tab" onclick="showTab('knowledge')">📚 Knowledge Generator</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" onchange="loadFile()">
<p>Select a YAML file to load existing tools data</p>
</div>
<div id="stats" class="stats-grid">
<!-- Stats will be populated by JavaScript -->
</div>
<div id="validationResults" class="hidden">
<h3>🔍 Validation Results</h3>
<div id="validationContent"></div>
</div>
<div style="text-align: center; margin-top: 20px;">
<button class="btn" onclick="validateYAML()">🔍 Validate YAML</button>
<button class="btn btn-secondary" onclick="previewYAML()">👁️ Preview YAML</button>
</div>
</div>
<!-- Tools Tab -->
<div id="tools" class="tab-content">
<div class="filter-section">
<h3>🔍 Filter Tools & Concepts</h3>
<div class="filter-row">
<label>Type:</label>
<select id="typeFilter" onchange="applyFilters()">
<option value="">All Types</option>
<option value="software">Software</option>
<option value="method">Method</option>
<option value="concept">Concept</option>
</select>
<label>Skill Level:</label>
<select id="skillFilter" onchange="applyFilters()">
<option value="">All Levels</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>
<button class="btn btn-secondary" onclick="clearFilters()">Clear Filters</button>
</div>
</div>
<div id="toolsGrid" class="tools-grid">
<!-- Tools will be populated by JavaScript -->
</div>
</div>
<!-- Editor Tab -->
<div id="editor" class="tab-content">
<h3>✏️ Add/Edit Tool, Method, or Concept</h3>
<form id="toolForm">
<div class="form-group">
<label for="toolType">Type *</label>
<select id="toolType" onchange="toggleConditionalFields()" required>
<option value="software">Software</option>
<option value="method">Method</option>
<option value="concept">Concept</option>
</select>
</div>
<div class="form-group">
<label for="toolName">Name *</label>
<input type="text" id="toolName" required>
</div>
<div class="form-group">
<label for="toolIcon">Icon (Emoji)</label>
<input type="text" id="toolIcon" placeholder="🔧" maxlength="2" style="width: 80px; text-align: center; font-size: 1.5em;">
</div>
<div class="form-group">
<label for="description">Description *</label>
<textarea id="description" required></textarea>
</div>
<div class="form-group">
<label for="skillLevel">Skill Level *</label>
<select id="skillLevel" required>
<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" required>
</div>
<div class="form-group">
<label>Domains</label>
<div id="domainsCheckbox" class="checkbox-group">
<!-- Populated by JavaScript -->
</div>
</div>
<div class="form-group">
<label>Phases</label>
<div id="phasesCheckbox" class="checkbox-group">
<!-- Populated by JavaScript -->
</div>
</div>
<!-- Software/Method specific fields -->
<div id="softwareMethodFields" class="conditional-fields">
<div class="form-group">
<label>Platforms</label>
<div id="platformsCheckbox" class="checkbox-group">
<div class="checkbox-item">
<input type="checkbox" value="Windows" id="platform-windows">
<label for="platform-windows">Windows</label>
</div>
<div class="checkbox-item">
<input type="checkbox" value="Linux" id="platform-linux">
<label for="platform-linux">Linux</label>
</div>
<div class="checkbox-item">
<input type="checkbox" value="macOS" id="platform-macos">
<label for="platform-macos">macOS</label>
</div>
<div class="checkbox-item">
<input type="checkbox" value="Web" id="platform-web">
<label for="platform-web">Web</label>
</div>
<div class="checkbox-item">
<input type="checkbox" value="Cloud" id="platform-cloud">
<label for="platform-cloud">Cloud</label>
</div>
</div>
</div>
<div class="form-group">
<label>Domain-Agnostic Software Categories</label>
<div id="domainAgnosticCheckbox" class="checkbox-group">
<!-- Populated by JavaScript -->
</div>
</div>
</div>
<!-- Software specific fields -->
<div id="softwareFields" class="conditional-fields">
<div class="form-group">
<label for="accessType">Access Type</label>
<select id="accessType">
<option value="">Select Access Type</option>
<option value="Download">Download</option>
<option value="Web">Web Application</option>
<option value="Cloud">Cloud Service</option>
<option value="CLI">Command Line</option>
<option value="API">API</option>
<option value="Hardware">Hardware</option>
</select>
</div>
<div class="form-group">
<label for="license">License</label>
<input type="text" id="license" placeholder="e.g., Open Source, Commercial, Free">
</div>
<div class="form-group">
<label for="projectUrl">Project URL</label>
<input type="url" id="projectUrl">
</div>
<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">Has Extended Knowledgebase</label>
</div>
</div>
</div>
<div class="form-group">
<label for="tagsInput">Tags</label>
<div class="tags-input" id="tagsInput" onclick="focusTagInput()">
<input type="text" id="tagInputField" placeholder="Add tags..." onkeydown="handleTagInput(event)" style="border: none; outline: none; flex: 1; min-width: 100px;">
</div>
</div>
<div class="form-group">
<label for="relatedConceptsInput">Related Concepts</label>
<div class="tags-input" id="relatedConceptsInput" onclick="focusRelatedConceptInput()">
<input type="text" id="relatedConceptInputField" placeholder="Add concept names..." onkeydown="handleRelatedConceptInput(event)" style="border: none; outline: none; flex: 1; min-width: 100px;">
</div>
<small>Reference concept names for providing background knowledge</small>
</div>
<div style="text-align: center; margin-top: 30px;">
<button type="button" class="btn" onclick="saveTool()">💾 Save</button>
<button type="button" class="btn btn-secondary" onclick="clearForm()">🔄 Clear</button>
<button type="button" class="btn btn-warning" onclick="cancelEdit()">❌ Cancel</button>
</div>
</form>
</div>
<!-- Bulk Edit Tab -->
<div id="bulk" class="tab-content">
<div class="bulk-actions">
<h3>📋 Bulk Operations</h3>
<div class="selection-info" id="selectionInfo">
No tools selected
</div>
<div>
<button class="btn" onclick="selectAllTools()">Select All</button>
<button class="btn btn-secondary" onclick="clearSelection()">Clear Selection</button>
<button class="btn btn-warning" onclick="bulkSetType()">Set Type</button>
<button class="btn btn-warning" onclick="bulkUpdateTags()">Update Tags</button>
<button class="btn btn-warning" onclick="bulkUpdateDomainAgnostic()">Set Domain-Agnostic</button>
<button class="btn btn-danger" onclick="bulkDelete()">Delete Selected</button>
</div>
</div>
<div id="bulkToolsGrid" class="tools-grid">
<!-- Tools with checkboxes will be populated by JavaScript -->
</div>
</div>
<!-- Knowledge Generator Tab -->
<div id="knowledge" class="tab-content">
<div class="knowledge-generator">
<h3>📚 Knowledgebase Entry Generator</h3>
<p>Generate markdown templates for tools with extended knowledgebase documentation.</p>
<div class="form-group">
<label for="knowledgeToolSelect">Select Tool/Concept:</label>
<select id="knowledgeToolSelect" onchange="generateKnowledgeTemplate()">
<option value="">Choose a tool or concept...</option>
</select>
</div>
<div id="knowledgePreview" class="hidden">
<h4>Generated Markdown Template:</h4>
<div class="markdown-preview" id="markdownContent"></div>
<button class="btn" onclick="downloadMarkdown()">💾 Download Markdown</button>
</div>
</div>
</div>
<!-- Export Tab -->
<div id="export" class="tab-content">
<h3>💾 Export & Preview</h3>
<div id="yamlPreview" class="hidden">
<h4>YAML Preview:</h4>
<textarea id="yamlPreviewText" readonly style="width: 100%; height: 400px; font-family: monospace;"></textarea>
</div>
<div style="text-align: center; margin-top: 20px;">
<button class="btn" onclick="previewYAML()">👁️ Preview YAML</button>
<button class="btn btn-success" onclick="exportYAML()">📥 Export YAML</button>
</div>
</div>
</div>
<script>
let yamlData = null;
let currentEditingIndex = -1;
let selectedTools = new Set();
// Initialize the application
function init() {
yamlData = {
tools: [],
domains: [
{ id: 'network', name: 'Network Forensics' },
{ id: 'memory', name: 'Memory Forensics' },
{ id: 'disk', name: 'Disk Forensics' },
{ id: 'mobile', name: 'Mobile Forensics' },
{ id: 'cloud', name: 'Cloud Forensics' },
{ id: 'malware', name: 'Malware Analysis' }
],
phases: [
{ id: 'identification', name: 'Identification' },
{ id: 'preservation', name: 'Preservation' },
{ id: 'collection', name: 'Collection' },
{ id: 'examination', name: 'Examination' },
{ id: 'analysis', name: 'Analysis' },
{ id: 'reporting', name: 'Reporting' }
],
'domain-agnostic-software': [
{ id: 'collaboration', name: 'Collaboration & Communication' },
{ id: 'documentation', name: 'Documentation & Reporting' },
{ id: 'virtualization', name: 'Virtualization & Sandboxing' },
{ id: 'automation', name: 'Automation & Scripting' }
]
};
populateFormOptions();
updateStats();
renderToolsGrid();
updateKnowledgeToolSelect();
}
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 for specific tabs
if (tabName === 'tools') {
renderToolsGrid();
} else if (tabName === 'bulk') {
renderBulkGrid();
} else if (tabName === 'knowledge') {
updateKnowledgeToolSelect();
}
}
function populateFormOptions() {
// Populate domains
const domainsContainer = document.getElementById('domainsCheckbox');
domainsContainer.innerHTML = '';
yamlData.domains.forEach(domain => {
const div = document.createElement('div');
div.className = 'checkbox-item';
div.innerHTML = `
<input type="checkbox" value="${domain.id}" id="domain-${domain.id}">
<label for="domain-${domain.id}">${domain.name}</label>
`;
domainsContainer.appendChild(div);
});
// Populate phases
const phasesContainer = document.getElementById('phasesCheckbox');
phasesContainer.innerHTML = '';
yamlData.phases.forEach(phase => {
const div = document.createElement('div');
div.className = 'checkbox-item';
div.innerHTML = `
<input type="checkbox" value="${phase.id}" id="phase-${phase.id}">
<label for="phase-${phase.id}">${phase.name}</label>
`;
phasesContainer.appendChild(div);
});
// Populate domain-agnostic software
const domainAgnosticContainer = document.getElementById('domainAgnosticCheckbox');
domainAgnosticContainer.innerHTML = '';
yamlData['domain-agnostic-software'].forEach(category => {
const div = document.createElement('div');
div.className = 'checkbox-item';
div.innerHTML = `
<input type="checkbox" value="${category.id}" id="domain-agnostic-${category.id}">
<label for="domain-agnostic-${category.id}">${category.name}</label>
`;
domainAgnosticContainer.appendChild(div);
});
}
function toggleConditionalFields() {
const toolType = document.getElementById('toolType').value;
const softwareMethodFields = document.getElementById('softwareMethodFields');
const softwareFields = document.getElementById('softwareFields');
if (toolType === 'concept') {
// Concepts don't have platforms, domain-agnostic categories, or software-specific fields
softwareMethodFields.classList.add('disabled');
softwareFields.classList.add('disabled');
} else if (toolType === 'method') {
// Methods have platforms and domain-agnostic categories but not software-specific fields
softwareMethodFields.classList.remove('disabled');
softwareFields.classList.add('disabled');
} else {
// Software has all fields
softwareMethodFields.classList.remove('disabled');
softwareFields.classList.remove('disabled');
}
}
function loadFile() {
const file = document.getElementById('fileInput').files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(e) {
try {
yamlData = jsyaml.load(e.target.result);
showMessage('YAML file loaded successfully!', 'success');
populateFormOptions();
updateStats();
renderToolsGrid();
updateKnowledgeToolSelect();
} catch (error) {
showMessage('Error loading YAML file: ' + error.message, 'error');
}
};
reader.readAsText(file);
}
function showMessage(message, type = 'success') {
const messageDiv = document.createElement('div');
messageDiv.className = `message ${type}`;
messageDiv.textContent = message;
document.body.appendChild(messageDiv);
setTimeout(() => {
document.body.removeChild(messageDiv);
}, 3000);
}
function updateStats() {
if (!yamlData || !yamlData.tools) 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
};
const statsContainer = document.getElementById('stats');
statsContainer.innerHTML = `
<div class="stat-card">
<h3>${stats.total}</h3>
<p>Total Items</p>
</div>
<div class="stat-card">
<h3>${stats.software}</h3>
<p>Software Tools</p>
</div>
<div class="stat-card">
<h3>${stats.methods}</h3>
<p>Methods</p>
</div>
<div class="stat-card">
<h3>${stats.concepts}</h3>
<p>Concepts</p>
</div>
<div class="stat-card">
<h3>${stats.withKnowledgebase}</h3>
<p>With Knowledgebase</p>
</div>
`;
}
function getCheckedValues(selector) {
return Array.from(document.querySelectorAll(selector + ':checked')).map(cb => cb.value);
}
function getTags() {
return Array.from(document.querySelectorAll('#tagsInput .tag')).map(tag =>
tag.textContent.replace('×', '').trim()
).filter(tag => tag);
}
function getRelatedConcepts() {
return Array.from(document.querySelectorAll('#relatedConceptsInput .tag')).map(tag =>
tag.textContent.replace('×', '').trim()
).filter(concept => concept);
}
function focusTagInput() {
document.getElementById('tagInputField').focus();
}
function focusRelatedConceptInput() {
document.getElementById('relatedConceptInputField').focus();
}
function handleTagInput(event) {
if (event.key === 'Enter' || event.key === ',') {
event.preventDefault();
const input = event.target;
const value = input.value.trim();
if (value) {
addTag('tagsInput', value);
input.value = '';
}
}
}
function handleRelatedConceptInput(event) {
if (event.key === 'Enter' || event.key === ',') {
event.preventDefault();
const input = event.target;
const value = input.value.trim();
if (value) {
addTag('relatedConceptsInput', value, 'related-concept');
input.value = '';
}
}
}
function addTag(containerId, value, extraClass = '') {
const container = document.getElementById(containerId);
const input = container.querySelector('input');
const tag = document.createElement('span');
tag.className = `tag ${extraClass}`;
tag.innerHTML = `${value} <span class="tag-remove" onclick="this.parentElement.remove()">×</span>`;
container.insertBefore(tag, input);
}
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,
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 domains and phases for all types
tool.domains = getCheckedValues('#domainsCheckbox input:checked');
tool.phases = getCheckedValues('#phasesCheckbox input:checked');
// Add related concepts
const relatedConcepts = getRelatedConcepts();
if (relatedConcepts.length > 0) {
tool.related_concepts = relatedConcepts;
}
// Type-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;
}
// Add domain-agnostic software if selected
const domainAgnostic = getCheckedValues('#domainAgnosticCheckbox input:checked');
if (domainAgnostic.length > 0) {
tool['domain-agnostic-software'] = domainAgnostic;
}
} else if (toolType === 'method') {
tool.platforms = getCheckedValues('#platformsCheckbox input:checked');
// Add domain-agnostic software if selected
const domainAgnostic = getCheckedValues('#domainAgnosticCheckbox input:checked');
if (domainAgnostic.length > 0) {
tool['domain-agnostic-software'] = domainAgnostic;
}
// Methods don't have software-specific fields
tool.accessType = null;
tool.projectUrl = null;
tool.license = null;
tool.knowledgebase = false;
} else if (toolType === 'concept') {
// Concepts don't have platforms or software-specific fields (but can have knowledgebase)
tool.platforms = [];
tool.accessType = null;
tool.projectUrl = null;
tool.license = null;
tool.knowledgebase = document.getElementById('knowledgebase').checked;
}
// Clean up empty arrays and null values
Object.keys(tool).forEach(key => {
if (Array.isArray(tool[key]) && tool[key].length === 0) {
delete tool[key];
} else if (tool[key] === '' || tool[key] === null || tool[key] === false) {
if (key === 'knowledgebase' && (toolType === 'software' || toolType === 'concept')) {
// Keep knowledgebase boolean for software and concepts
} else {
delete tool[key];
}
}
});
if (currentEditingIndex >= 0) {
yamlData.tools[currentEditingIndex] = tool;
showMessage('Tool updated successfully!');
currentEditingIndex = -1;
} else {
yamlData.tools.push(tool);
showMessage('Tool added successfully!');
}
clearForm();
updateStats();
renderToolsGrid();
updateKnowledgeToolSelect();
} catch (error) {
showMessage('Error saving tool: ' + error.message, 'error');
}
}
function clearForm() {
document.getElementById('toolForm').reset();
document.getElementById('tagsInput').innerHTML = '<input type="text" id="tagInputField" placeholder="Add tags..." onkeydown="handleTagInput(event)" style="border: none; outline: none; flex: 1; min-width: 100px;">';
document.getElementById('relatedConceptsInput').innerHTML = '<input type="text" id="relatedConceptInputField" placeholder="Add concept names..." onkeydown="handleRelatedConceptInput(event)" style="border: none; outline: none; flex: 1; min-width: 100px;">';
currentEditingIndex = -1;
toggleConditionalFields();
}
function cancelEdit() {
clearForm();
showMessage('Edit cancelled');
}
function editTool(index) {
const tool = yamlData.tools[index];
currentEditingIndex = index;
// Populate form fields
document.getElementById('toolType').value = tool.type || 'software';
document.getElementById('toolName').value = tool.name || '';
document.getElementById('toolIcon').value = tool.icon || '';
document.getElementById('description').value = tool.description || '';
document.getElementById('skillLevel').value = tool.skillLevel || 'intermediate';
document.getElementById('url').value = tool.url || '';
// Software-specific fields
if (tool.type === 'software' || !tool.type) {
document.getElementById('accessType').value = tool.accessType || '';
document.getElementById('projectUrl').value = tool.projectUrl || '';
document.getElementById('license').value = tool.license || '';
document.getElementById('statusUrl').value = tool.statusUrl || '';
}
// Knowledgebase field for software and concepts
if (tool.type === 'software' || tool.type === 'concept' || !tool.type) {
document.getElementById('knowledgebase').checked = tool.knowledgebase || false;
}
// Set checkboxes
setCheckboxValues('#domainsCheckbox input', tool.domains || []);
setCheckboxValues('#phasesCheckbox input', tool.phases || []);
setCheckboxValues('#platformsCheckbox input', tool.platforms || []);
setCheckboxValues('#domainAgnosticCheckbox input', tool['domain-agnostic-software'] || []);
// Set tags
const tagsContainer = document.getElementById('tagsInput');
tagsContainer.innerHTML = '<input type="text" id="tagInputField" placeholder="Add tags..." onkeydown="handleTagInput(event)" style="border: none; outline: none; flex: 1; min-width: 100px;">';
(tool.tags || []).forEach(tag => addTag('tagsInput', tag));
// Set related concepts
const conceptsContainer = document.getElementById('relatedConceptsInput');
conceptsContainer.innerHTML = '<input type="text" id="relatedConceptInputField" placeholder="Add concept names..." onkeydown="handleRelatedConceptInput(event)" style="border: none; outline: none; flex: 1; min-width: 100px;">';
(tool.related_concepts || []).forEach(concept => addTag('relatedConceptsInput', concept, 'related-concept'));
toggleConditionalFields();
showTab('editor');
}
function setCheckboxValues(selector, values) {
document.querySelectorAll(selector).forEach(cb => {
cb.checked = values.includes(cb.value);
});
}
function deleteTool(index) {
if (confirm('Are you sure you want to delete this tool?')) {
yamlData.tools.splice(index, 1);
showMessage('Tool deleted successfully!');
updateStats();
renderToolsGrid();
updateKnowledgeToolSelect();
}
}
function renderToolsGrid() {
const container = document.getElementById('toolsGrid');
container.innerHTML = '';
if (!yamlData || !yamlData.tools) return;
let filteredTools = yamlData.tools;
// Apply filters
const typeFilter = document.getElementById('typeFilter')?.value;
const skillFilter = document.getElementById('skillFilter')?.value;
if (typeFilter) {
filteredTools = filteredTools.filter(tool => (tool.type || 'software') === typeFilter);
}
if (skillFilter) {
filteredTools = filteredTools.filter(tool => tool.skillLevel === skillFilter);
}
filteredTools.forEach((tool, index) => {
const originalIndex = yamlData.tools.indexOf(tool);
const card = createToolCard(tool, originalIndex);
container.appendChild(card);
});
}
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('');
// Add related concepts indicators
const relatedConceptTags = (tool.related_concepts || []).map(concept => {
return `<span class="tag related-concept">🧠 ${concept}</span>`;
}).join('');
card.innerHTML = `
<h3>
${tool.icon ? tool.icon + ' ' : ''}${tool.name}
${typeIndicator}
</h3>
<p>${tool.description}</p>
<div style="margin-bottom: 10px;">
<span class="skill-level ${skillClass}">${tool.skillLevel || 'intermediate'}</span>
</div>
<div style="margin-bottom: 10px;">
${tags}
${domainAgnosticTags}
${relatedConceptTags}
${knowledgebaseIndicator}
</div>
<div class="tool-actions">
<button class="btn btn-secondary" onclick="editTool(${index})">✏️ Edit</button>
<button class="btn btn-danger" onclick="deleteTool(${index})">🗑️ Delete</button>
</div>
`;
return card;
}
function getDomainAgnosticName(id) {
const category = yamlData['domain-agnostic-software'].find(cat => cat.id === id);
return category ? category.name : id;
}
function applyFilters() {
renderToolsGrid();
}
function clearFilters() {
document.getElementById('typeFilter').value = '';
document.getElementById('skillFilter').value = '';
renderToolsGrid();
}
// Bulk operations
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 createBulkToolCard(tool, index) {
const card = document.createElement('div');
card.className = `tool-card ${tool.type || 'software'}`;
const isSelected = selectedTools.has(index);
card.style.opacity = isSelected ? '1' : '0.7';
card.style.border = isSelected ? '2px solid #3498db' : '1px solid #e9ecef';
const typeIndicator = `<span class="type-badge type-${tool.type || 'software'}">${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})" style="transform: scale(1.2);">
<h3 style="margin: 0; flex: 1;">
${tool.icon ? tool.icon + ' ' : ''}${tool.name}
${typeIndicator}
</h3>
</div>
<p>${tool.description}</p>
<div>
<span class="skill-level skill-${tool.skillLevel || 'intermediate'}">${tool.skillLevel || 'intermediate'}</span>
</div>
`;
return card;
}
function toggleToolSelection(index) {
if (selectedTools.has(index)) {
selectedTools.delete(index);
} else {
selectedTools.add(index);
}
updateSelectionCount();
renderBulkGrid();
}
function selectAllTools() {
selectedTools.clear();
yamlData.tools.forEach((_, index) => selectedTools.add(index));
updateSelectionCount();
renderBulkGrid();
}
function clearSelection() {
selectedTools.clear();
updateSelectionCount();
renderBulkGrid();
}
function updateSelectionCount() {
const count = selectedTools.size;
document.getElementById('selectionInfo').textContent =
count === 0 ? 'No tools selected' : `${count} tool(s) selected`;
}
function bulkSetType() {
if (selectedTools.size === 0) {
showMessage('No tools selected', 'error');
return;
}
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`);
updateStats();
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`);
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 bulkDelete() {
if (selectedTools.size === 0) {
showMessage('No tools selected', 'error');
return;
}
if (confirm(`Are you sure you want to delete ${selectedTools.size} selected tools?`)) {
const indicesToDelete = Array.from(selectedTools).sort((a, b) => b - a);
indicesToDelete.forEach(index => {
yamlData.tools.splice(index, 1);
});
selectedTools.clear();
showMessage(`Deleted ${indicesToDelete.length} tools successfully!`);
updateStats();
renderBulkGrid();
updateKnowledgeToolSelect();
}
}
// Knowledge Generator
function updateKnowledgeToolSelect() {
const select = document.getElementById('knowledgeToolSelect');
select.innerHTML = '<option value="">Choose a tool or concept...</option>';
if (!yamlData || !yamlData.tools) return;
yamlData.tools
.filter(tool => tool.knowledgebase || tool.type === 'concept')
.forEach((tool, index) => {
const originalIndex = yamlData.tools.indexOf(tool);
const option = document.createElement('option');
option.value = originalIndex;
option.textContent = `${tool.icon ? tool.icon + ' ' : ''}${tool.name} (${tool.type || 'software'})`;
select.appendChild(option);
});
}
function generateKnowledgeTemplate() {
const select = document.getElementById('knowledgeToolSelect');
const index = parseInt(select.value);
if (isNaN(index)) {
document.getElementById('knowledgePreview').classList.add('hidden');
return;
}
const tool = yamlData.tools[index];
const template = createMarkdownTemplate(tool);
document.getElementById('markdownContent').textContent = template;
document.getElementById('knowledgePreview').classList.remove('hidden');
}
function createMarkdownTemplate(tool) {
const isConceptType = tool.type === 'concept';
const title = isConceptType ? 'Concept' : 'Tool';
return `# ${tool.icon ? tool.icon + ' ' : ''}${tool.name}
## Overview
${tool.description}
**Type**: ${tool.type || 'software'}
**Skill Level**: ${tool.skillLevel || 'intermediate'}
**Official URL**: [${tool.name}](${tool.url})
${tool.license ? `**License**: ${tool.license}` : ''}
${tool.platforms && tool.platforms.length > 0 ? `**Platforms**: ${tool.platforms.join(', ')}` : ''}
## ${isConceptType ? 'Key Concepts' : 'Key Features'}
- Feature/concept 1
- Feature/concept 2
- Feature/concept 3
## ${isConceptType ? 'Applications' : 'Installation & Setup'}
${isConceptType ?
`This concept applies to:
- Use case 1
- Use case 2
- Use case 3` :
`### Prerequisites
- Requirement 1
- Requirement 2
### Installation Steps
1. Step 1
2. Step 2
3. Step 3`}
## ${isConceptType ? 'Related Tools' : 'Basic Usage'}
${isConceptType ?
`Tools that implement or relate to this concept:
${tool.related_concepts ? tool.related_concepts.map(c => `- ${c}`).join('\n') : '- Related tool 1\n- Related tool 2'}` :
`### Common Commands/Operations
\`\`\`bash
# Example command 1
command --option value
# Example command 2
command2 --help
\`\`\`
### Workflow Examples
1. Basic workflow step
2. Advanced workflow step`}
## Advanced Topics
${isConceptType ?
`### Implementation Variations
- Variation 1
- Variation 2
### Best Practices
- Practice 1
- Practice 2` :
`### Advanced Configuration
- Advanced setting 1
- Advanced setting 2
### Integration with Other Tools
- Integration 1
- Integration 2`}
## Troubleshooting
### Common Issues
**Issue 1**: Description
- Solution: Solution description
**Issue 2**: Description
- Solution: Solution description
## Resources
- [Official Documentation](${tool.url})
${tool.projectUrl ? `- [Project Repository](${tool.projectUrl})` : ''}
- Additional resource 1
- Additional resource 2
## Tags
${tool.tags ? tool.tags.map(tag => `\`${tag}\``).join(' ') : '`tag1` `tag2` `tag3`'}
${tool.domains && tool.domains.length > 0 ? `## Forensics Domains
${tool.domains.map(domain => `- ${domain}`).join('\n')}` : ''}
${tool.phases && tool.phases.length > 0 ? `## Investigation Phases
${tool.phases.map(phase => `- ${phase}`).join('\n')}` : ''}
---
*Last updated: ${new Date().toISOString().split('T')[0]}*
`;
}
function downloadMarkdown() {
const content = document.getElementById('markdownContent').textContent;
const select = document.getElementById('knowledgeToolSelect');
const index = parseInt(select.value);
const tool = yamlData.tools[index];
const blob = new Blob([content], { type: 'text/markdown' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${tool.name.toLowerCase().replace(/[^a-z0-9]/g, '-')}.md`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
showMessage('Markdown template downloaded successfully!');
}
// Validation and Export
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', 'concept'].includes(tool.type)) {
validationResults.push(`❌ Tool ${index + 1}: Invalid type (must be 'software', 'method', or 'concept')`);
}
// Type-specific validation
if (tool.type === 'software') {
if (!tool.platforms || tool.platforms.length === 0) {
validationResults.push(`⚠️ Tool ${index + 1}: Software should have platforms`);
}
if (!tool.license) validationResults.push(`⚠️ Tool ${index + 1}: Software should have license`);
}
if (tool.type === 'concept') {
if (tool.platforms && tool.platforms.length > 0) {
validationResults.push(`⚠️ Tool ${index + 1}: Concepts should not have platforms`);
}
if (tool.license) {
validationResults.push(`⚠️ Tool ${index + 1}: Concepts should not have license`);
}
}
// Validate related_concepts references
if (tool.related_concepts) {
const conceptNames = yamlData.tools.filter(t => t.type === 'concept').map(t => t.name);
tool.related_concepts.forEach(conceptName => {
if (!conceptNames.includes(conceptName)) {
validationResults.push(`⚠️ Tool ${index + 1}: References unknown concept '${conceptName}'`);
}
});
}
});
}
const container = document.getElementById('validationContent');
if (validationResults.length === 0) {
container.innerHTML = '<div class="message success">✅ YAML structure is valid!</div>';
} else {
container.innerHTML = '<div class="message error">' + 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!');
}
// Initialize the application
init();
</script>
</body>
</html>