1654 lines
61 KiB
HTML
1654 lines
61 KiB
HTML
<!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> |