1734 lines
70 KiB
HTML
1734 lines
70 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;
|
||
}
|
||
|
||
:root {
|
||
--primary: #3498db;
|
||
--secondary: #95a5a6;
|
||
--success: #27ae60;
|
||
--warning: #f39c12;
|
||
--danger: #e74c3c;
|
||
--dark: #2c3e50;
|
||
--light: #ecf0f1;
|
||
--border: #dee2e6;
|
||
}
|
||
|
||
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, var(--dark) 0%, var(--primary) 100%);
|
||
color: white;
|
||
padding: 25px;
|
||
text-align: center;
|
||
}
|
||
|
||
.tabs {
|
||
background: var(--light);
|
||
display: flex;
|
||
border-bottom: 2px solid var(--border);
|
||
}
|
||
|
||
.tab {
|
||
padding: 15px 25px;
|
||
cursor: pointer;
|
||
border-bottom: 3px solid transparent;
|
||
transition: all 0.3s ease;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.tab:hover {
|
||
background: var(--border);
|
||
}
|
||
|
||
.tab.active {
|
||
background: white;
|
||
border-bottom-color: var(--primary);
|
||
color: var(--primary);
|
||
}
|
||
|
||
.tab-content {
|
||
display: none;
|
||
padding: 30px;
|
||
min-height: 600px;
|
||
}
|
||
|
||
.tab-content.active {
|
||
display: block;
|
||
}
|
||
|
||
.btn {
|
||
background: var(--primary);
|
||
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: var(--secondary); }
|
||
.btn-danger { background: var(--danger); }
|
||
.btn-success { background: var(--success); }
|
||
.btn-warning { background: var(--warning); }
|
||
|
||
.search-container {
|
||
background: var(--light);
|
||
padding: 15px;
|
||
border-radius: 8px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.search-input {
|
||
width: 100%;
|
||
padding: 12px;
|
||
border: 2px solid var(--border);
|
||
border-radius: 8px;
|
||
font-size: 16px;
|
||
transition: border-color 0.3s ease;
|
||
}
|
||
|
||
.search-input:focus {
|
||
outline: none;
|
||
border-color: var(--primary);
|
||
}
|
||
|
||
.search-results-info {
|
||
margin-top: 10px;
|
||
font-size: 14px;
|
||
color: var(--secondary);
|
||
}
|
||
|
||
.stats-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||
gap: 20px;
|
||
margin-bottom: 30px;
|
||
}
|
||
|
||
.stat-card {
|
||
background: var(--primary);
|
||
color: white;
|
||
padding: 20px;
|
||
border-radius: 10px;
|
||
text-align: center;
|
||
}
|
||
|
||
.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 var(--border);
|
||
border-radius: 10px;
|
||
padding: 20px;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.tool-card:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
||
}
|
||
|
||
.tool-card.software { border-left: 4px solid var(--primary); }
|
||
.tool-card.method { border-left: 4px solid #9b59b6; }
|
||
.tool-card.concept { border-left: 4px solid #e67e22; }
|
||
|
||
.form-group {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.form-group label {
|
||
display: block;
|
||
margin-bottom: 5px;
|
||
font-weight: 500;
|
||
color: var(--dark);
|
||
}
|
||
|
||
.form-group input,
|
||
.form-group select,
|
||
.form-group textarea {
|
||
width: 100%;
|
||
padding: 10px;
|
||
border: 1px solid var(--border);
|
||
border-radius: 5px;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.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 var(--border);
|
||
border-radius: 5px;
|
||
background: white;
|
||
}
|
||
|
||
.tag {
|
||
background: var(--light);
|
||
padding: 4px 8px;
|
||
border-radius: 15px;
|
||
font-size: 0.85em;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 5px;
|
||
}
|
||
|
||
.tag-remove {
|
||
cursor: pointer;
|
||
font-weight: bold;
|
||
color: #666;
|
||
}
|
||
|
||
.tag-remove:hover {
|
||
color: var(--danger);
|
||
}
|
||
|
||
.bulk-section {
|
||
background: var(--light);
|
||
padding: 20px;
|
||
border-radius: 10px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.bulk-controls {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||
gap: 10px;
|
||
margin-top: 15px;
|
||
}
|
||
|
||
.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;
|
||
}
|
||
|
||
.hidden { display: none !important; }
|
||
|
||
.filter-section {
|
||
display: flex;
|
||
gap: 15px;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
margin-top: 15px;
|
||
}
|
||
|
||
@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>Enhanced 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 style="background: var(--light); padding: 20px; border-radius: 10px; margin-bottom: 20px; border: 2px dashed var(--border); text-align: center;">
|
||
<h3>📁 Load YAML File</h3>
|
||
<input type="file" id="fileInput" accept=".yaml,.yml" onchange="loadFile()">
|
||
<p>Select a YAML file to load existing tools data</p>
|
||
</div>
|
||
|
||
<div id="stats" class="stats-grid"></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 with Enhanced Search -->
|
||
<div id="tools" class="tab-content">
|
||
<div class="search-container">
|
||
<h3>🔍 Search & Filter Tools</h3>
|
||
<input type="text"
|
||
id="searchInput"
|
||
class="search-input"
|
||
placeholder="Search by name, description, tags, or related concepts..."
|
||
oninput="applySearch()">
|
||
<div class="search-results-info" id="searchResults"></div>
|
||
|
||
<div class="filter-section">
|
||
<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 All</button>
|
||
</div>
|
||
</div>
|
||
<div id="toolsGrid" class="tools-grid"></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="4" style="width: 80px; text-align: center; font-size: 1.5em;">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="description">Description *</label>
|
||
<textarea id="description" required rows="4"></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"></div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>Phases</label>
|
||
<div id="phasesCheckbox" class="checkbox-group"></div>
|
||
</div>
|
||
|
||
<!-- Software/Method specific fields -->
|
||
<div id="softwareMethodFields">
|
||
<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 Categories</label>
|
||
<div id="domainAgnosticCheckbox" class="checkbox-group"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Software specific fields -->
|
||
<div id="softwareFields">
|
||
<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="api">API</option>
|
||
<option value="cli">Command Line</option>
|
||
<option value="service">Service</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="license">License</label>
|
||
<input type="text" id="license" placeholder="e.g., Apache 2.0, MIT, Proprietary">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="projectUrl">Project URL (CC24 Server)</label>
|
||
<input type="url" id="projectUrl">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="statusUrl">Status URL</label>
|
||
<input type="url" id="statusUrl">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<div class="checkbox-item">
|
||
<input type="checkbox" id="knowledgebase">
|
||
<label for="knowledgebase">Has Extended Knowledgebase</label>
|
||
</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>
|
||
</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>
|
||
|
||
<!-- Enhanced Bulk Edit Tab -->
|
||
<div id="bulk" class="tab-content">
|
||
<div class="bulk-section">
|
||
<h3>📋 Bulk Operations</h3>
|
||
<div id="selectionInfo" style="margin-bottom: 15px; padding: 10px; background: var(--border); border-radius: 5px; font-weight: 500;">
|
||
No tools selected
|
||
</div>
|
||
|
||
<div class="bulk-controls">
|
||
<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="bulkSetSkillLevel()">Set Skill Level</button>
|
||
</div>
|
||
|
||
<h4 style="margin: 20px 0 10px 0;">Tags Operations</h4>
|
||
<div class="bulk-controls">
|
||
<button class="btn btn-warning" onclick="bulkAddTags()">Add Tags</button>
|
||
<button class="btn btn-danger" onclick="bulkRemoveTags()">Remove Tags</button>
|
||
<button class="btn btn-secondary" onclick="bulkReplaceTags()">Replace All Tags</button>
|
||
<button class="btn btn-danger" onclick="bulkClearTags()">🗑️ Clear All Tags</button>
|
||
</div>
|
||
|
||
<h4 style="margin: 20px 0 10px 0;">Domain & Phase Operations</h4>
|
||
<div class="bulk-controls">
|
||
<button class="btn btn-warning" onclick="bulkAddDomains()">Add Domains</button>
|
||
<button class="btn btn-danger" onclick="bulkRemoveDomains()">Remove Domains</button>
|
||
<button class="btn btn-danger" onclick="bulkClearDomains()">🗑️ Clear All Domains</button>
|
||
<button class="btn btn-warning" onclick="bulkAddPhases()">Add Phases</button>
|
||
<button class="btn btn-danger" onclick="bulkRemovePhases()">Remove Phases</button>
|
||
<button class="btn btn-danger" onclick="bulkClearPhases()">🗑️ Clear All Phases</button>
|
||
</div>
|
||
|
||
<h4 style="margin: 20px 0 10px 0;">Platform Operations</h4>
|
||
<div class="bulk-controls">
|
||
<button class="btn btn-warning" onclick="bulkAddPlatforms()">Add Platforms</button>
|
||
<button class="btn btn-danger" onclick="bulkRemovePlatforms()">Remove Platforms</button>
|
||
<button class="btn btn-danger" onclick="bulkClearPlatforms()">🗑️ Clear All Platforms</button>
|
||
</div>
|
||
|
||
<h4 style="margin: 20px 0 10px 0;">Related Concepts Operations</h4>
|
||
<div class="bulk-controls">
|
||
<button class="btn btn-warning" onclick="bulkAddRelatedConcepts()">Add Related Concepts</button>
|
||
<button class="btn btn-danger" onclick="bulkRemoveRelatedConcepts()">Remove Related Concepts</button>
|
||
<button class="btn btn-danger" onclick="bulkClearRelatedConcepts()">🗑️ Clear All Related Concepts</button>
|
||
</div>
|
||
|
||
<h4 style="margin: 20px 0 10px 0;">Dangerous Operations</h4>
|
||
<div class="bulk-controls">
|
||
<button class="btn btn-danger" onclick="bulkDelete()">Delete Selected</button>
|
||
</div>
|
||
</div>
|
||
<div id="bulkToolsGrid" class="tools-grid"></div>
|
||
</div>
|
||
|
||
<!-- Knowledge Generator Tab -->
|
||
<div id="knowledge" class="tab-content">
|
||
<div style="background: #fff3cd; border: 1px solid #ffeaa7; border-radius: 8px; padding: 20px; margin-top: 20px;">
|
||
<h3>📚 Knowledgebase Entry Generator</h3>
|
||
<p>Generate properly formatted markdown files for CC24-Hub knowledgebase.</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>
|
||
<textarea id="markdownContent" readonly style="width: 100%; height: 400px; font-family: monospace; background: var(--light); border: 1px solid var(--border); border-radius: 4px; padding: 15px;"></textarea>
|
||
<div style="margin-top: 10px;">
|
||
<button class="btn" onclick="downloadMarkdown()">💾 Download Markdown</button>
|
||
<button class="btn btn-secondary" onclick="copyMarkdown()">📋 Copy to Clipboard</button>
|
||
</div>
|
||
</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();
|
||
let filteredToolsCache = [];
|
||
|
||
// Initialize with correct YAML structure
|
||
function init() {
|
||
yamlData = {
|
||
tools: [],
|
||
domains: [
|
||
{ id: 'incident-response', name: 'Incident Response & Breach-Untersuchung' },
|
||
{ id: 'static-investigations', name: 'Datenträgerforensik & Ermittlungen' },
|
||
{ id: 'malware-analysis', name: 'Malware-Analyse & Reverse Engineering' },
|
||
{ id: 'fraud-investigation', name: 'Betrugs- & Finanzkriminalität' },
|
||
{ id: 'network-forensics', name: 'Netzwerk-Forensik & Traffic-Analyse' },
|
||
{ id: 'mobile-forensics', name: 'Mobile Geräte & App-Forensik' },
|
||
{ id: 'cloud-forensics', name: 'Cloud & Virtuelle Umgebungen' },
|
||
{ id: 'ics-forensics', name: 'Industrielle Kontrollsysteme (ICS/SCADA)' }
|
||
],
|
||
phases: [
|
||
{ id: 'data-collection', name: 'Datensammlung', description: 'Imaging, Acquisition, Remote Collection Tools' },
|
||
{ id: 'examination', name: 'Auswertung', description: 'Parsing, Extraction, Initial Analysis Tools' },
|
||
{ id: 'analysis', name: 'Analyse', description: 'Deep Analysis, Correlation, Visualization Tools' },
|
||
{ id: 'reporting', name: 'Bericht & Präsentation', description: 'Documentation, Visualization, Presentation Tools' }
|
||
],
|
||
'domain-agnostic-software': [
|
||
{ id: 'collaboration-general', name: 'Übergreifend & Kollaboration', description: 'Cross-cutting tools and collaboration platforms' },
|
||
{ id: 'specific-os', name: 'Betriebssysteme', description: 'Operating Systems which focus on forensics' }
|
||
]
|
||
};
|
||
|
||
populateFormOptions();
|
||
updateStats();
|
||
renderToolsGrid();
|
||
updateKnowledgeToolSelect();
|
||
}
|
||
|
||
function showTab(tabName) {
|
||
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
|
||
document.querySelectorAll('.tab').forEach(tab => tab.classList.remove('active'));
|
||
|
||
document.getElementById(tabName).classList.add('active');
|
||
event.target.classList.add('active');
|
||
|
||
if (tabName === 'tools') renderToolsGrid();
|
||
else if (tabName === 'bulk') renderBulkGrid();
|
||
else if (tabName === 'knowledge') updateKnowledgeToolSelect();
|
||
}
|
||
|
||
// Enhanced Search Functionality
|
||
function applySearch() {
|
||
applyFilters();
|
||
}
|
||
|
||
function searchTools(tools, searchTerm) {
|
||
if (!searchTerm) return tools;
|
||
|
||
const term = searchTerm.toLowerCase();
|
||
return tools.filter(tool => {
|
||
// Search in name
|
||
if (tool.name && tool.name.toLowerCase().includes(term)) return true;
|
||
|
||
// Search in description
|
||
if (tool.description && tool.description.toLowerCase().includes(term)) return true;
|
||
|
||
// Search in tags
|
||
if (tool.tags && tool.tags.some(tag => tag.toLowerCase().includes(term))) return true;
|
||
|
||
// Search in related concepts
|
||
if (tool.related_concepts && tool.related_concepts.some(concept => concept.toLowerCase().includes(term))) return true;
|
||
|
||
// Search in type
|
||
if (tool.type && tool.type.toLowerCase().includes(term)) return true;
|
||
|
||
// Search in platforms
|
||
if (tool.platforms && tool.platforms.some(platform => platform.toLowerCase().includes(term))) return true;
|
||
|
||
return false;
|
||
});
|
||
}
|
||
|
||
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') {
|
||
softwareMethodFields.style.display = 'none';
|
||
softwareFields.style.display = 'none';
|
||
} else if (toolType === 'method') {
|
||
softwareMethodFields.style.display = 'block';
|
||
softwareFields.style.display = 'none';
|
||
} else {
|
||
softwareMethodFields.style.display = 'block';
|
||
softwareFields.style.display = 'block';
|
||
}
|
||
}
|
||
|
||
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?.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
|
||
};
|
||
|
||
document.getElementById('stats').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>
|
||
`;
|
||
}
|
||
|
||
// Tag input handlers
|
||
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);
|
||
input.value = '';
|
||
}
|
||
}
|
||
}
|
||
|
||
function addTag(containerId, value) {
|
||
const container = document.getElementById(containerId);
|
||
const input = container.querySelector('input');
|
||
|
||
const tag = document.createElement('span');
|
||
tag.className = 'tag';
|
||
tag.innerHTML = `${value} <span class="tag-remove" onclick="this.parentElement.remove()">×</span>`;
|
||
container.insertBefore(tag, input);
|
||
}
|
||
|
||
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 saveTool() {
|
||
try {
|
||
if (!yamlData) yamlData = { tools: [] };
|
||
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
|
||
};
|
||
|
||
// Add icon if provided
|
||
const icon = document.getElementById('toolIcon').value.trim();
|
||
if (icon) tool.icon = icon;
|
||
|
||
// Add domains and phases
|
||
tool.domains = getCheckedValues('#domainsCheckbox input:checked');
|
||
tool.phases = getCheckedValues('#phasesCheckbox input:checked');
|
||
|
||
// Add tags and related concepts
|
||
const tags = getTags();
|
||
if (tags.length > 0) tool.tags = tags;
|
||
|
||
const relatedConcepts = getRelatedConcepts();
|
||
if (relatedConcepts.length > 0) tool.related_concepts = relatedConcepts;
|
||
|
||
// Type-specific fields
|
||
if (toolType === 'software') {
|
||
tool.platforms = getCheckedValues('#platformsCheckbox input:checked');
|
||
|
||
const accessType = document.getElementById('accessType').value;
|
||
if (accessType) tool.accessType = accessType;
|
||
|
||
const license = document.getElementById('license').value.trim();
|
||
if (license) tool.license = license;
|
||
|
||
const projectUrl = document.getElementById('projectUrl').value.trim();
|
||
if (projectUrl) tool.projectUrl = projectUrl;
|
||
|
||
const statusUrl = document.getElementById('statusUrl').value.trim();
|
||
if (statusUrl) tool.statusUrl = statusUrl;
|
||
|
||
tool.knowledgebase = document.getElementById('knowledgebase').checked;
|
||
|
||
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');
|
||
const domainAgnostic = getCheckedValues('#domainAgnosticCheckbox input:checked');
|
||
if (domainAgnostic.length > 0) tool['domain-agnostic-software'] = domainAgnostic;
|
||
tool.knowledgebase = document.getElementById('knowledgebase').checked;
|
||
} else if (toolType === 'concept') {
|
||
tool.knowledgebase = document.getElementById('knowledgebase').checked;
|
||
}
|
||
|
||
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 || '';
|
||
}
|
||
|
||
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));
|
||
|
||
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?.tools) return;
|
||
|
||
let filteredTools = yamlData.tools;
|
||
|
||
// Apply search filter
|
||
const searchTerm = document.getElementById('searchInput')?.value;
|
||
if (searchTerm) {
|
||
filteredTools = searchTools(filteredTools, searchTerm);
|
||
}
|
||
|
||
// Apply type and skill 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);
|
||
|
||
// Cache filtered results
|
||
filteredToolsCache = filteredTools;
|
||
|
||
// Update search results info
|
||
const searchResults = document.getElementById('searchResults');
|
||
if (searchResults) {
|
||
const totalTools = yamlData.tools.length;
|
||
const filteredCount = filteredTools.length;
|
||
if (searchTerm || typeFilter || skillFilter) {
|
||
searchResults.textContent = `Showing ${filteredCount} of ${totalTools} tools`;
|
||
} else {
|
||
searchResults.textContent = `Showing all ${totalTools} tools`;
|
||
}
|
||
}
|
||
|
||
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 tags = (tool.tags || []).map(tag => `<span class="tag">${tag}</span>`).join('');
|
||
const knowledgebaseIndicator = tool.knowledgebase ? '<span class="tag" style="background: #e8f5e8; color: #27ae60;">📚 KB</span>' : '';
|
||
|
||
card.innerHTML = `
|
||
<h3>${tool.icon ? tool.icon + ' ' : ''}${tool.name} <span style="font-size: 0.7em; color: #666;">[${tool.type || 'software'}]</span></h3>
|
||
<p>${tool.description}</p>
|
||
<div style="margin: 10px 0;">${tags} ${knowledgebaseIndicator}</div>
|
||
<div style="display: flex; gap: 5px;">
|
||
<button class="btn btn-secondary" onclick="editTool(${index})" style="flex: 1; padding: 8px;">✏️ Edit</button>
|
||
<button class="btn btn-danger" onclick="deleteTool(${index})" style="flex: 1; padding: 8px;">🗑️ Delete</button>
|
||
</div>
|
||
`;
|
||
|
||
return card;
|
||
}
|
||
|
||
function applyFilters() { renderToolsGrid(); }
|
||
|
||
function clearFilters() {
|
||
document.getElementById('typeFilter').value = '';
|
||
document.getElementById('skillFilter').value = '';
|
||
document.getElementById('searchInput').value = '';
|
||
renderToolsGrid();
|
||
}
|
||
|
||
// Enhanced Bulk Operations
|
||
function renderBulkGrid() {
|
||
const container = document.getElementById('bulkToolsGrid');
|
||
container.innerHTML = '';
|
||
|
||
if (!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 var(--primary)' : '1px solid var(--border)';
|
||
|
||
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}</h3>
|
||
</div>
|
||
<p>${tool.description}</p>
|
||
<div><span style="background: var(--light); padding: 2px 6px; border-radius: 10px; font-size: 0.8em;">${tool.type || 'software'}</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`;
|
||
}
|
||
|
||
// Enhanced bulk operations with new clear functions
|
||
function bulkSetType() {
|
||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
||
const newType = prompt('Enter new type (software/method/concept):');
|
||
if (newType && ['software', 'method', 'concept'].includes(newType)) {
|
||
selectedTools.forEach(index => yamlData.tools[index].type = newType);
|
||
showMessage(`Updated type for ${selectedTools.size} tools`);
|
||
updateStats();
|
||
renderBulkGrid();
|
||
}
|
||
}
|
||
|
||
function bulkSetSkillLevel() {
|
||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
||
const newLevel = prompt('Enter skill level (novice/beginner/intermediate/advanced/expert):');
|
||
if (newLevel && ['novice', 'beginner', 'intermediate', 'advanced', 'expert'].includes(newLevel)) {
|
||
selectedTools.forEach(index => yamlData.tools[index].skillLevel = newLevel);
|
||
showMessage(`Updated skill level for ${selectedTools.size} tools`);
|
||
renderBulkGrid();
|
||
}
|
||
}
|
||
|
||
function bulkAddTags() {
|
||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
||
const tags = prompt('Enter tags to add (comma-separated):');
|
||
if (tags) {
|
||
const tagList = tags.split(',').map(t => t.trim()).filter(t => t);
|
||
selectedTools.forEach(index => {
|
||
const tool = yamlData.tools[index];
|
||
tool.tags = [...new Set([...(tool.tags || []), ...tagList])];
|
||
});
|
||
showMessage(`Added tags to ${selectedTools.size} tools`);
|
||
renderBulkGrid();
|
||
}
|
||
}
|
||
|
||
function bulkRemoveTags() {
|
||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
||
const tags = prompt('Enter tags to remove (comma-separated):');
|
||
if (tags) {
|
||
const tagList = tags.split(',').map(t => t.trim()).filter(t => t);
|
||
selectedTools.forEach(index => {
|
||
const tool = yamlData.tools[index];
|
||
if (tool.tags) {
|
||
tool.tags = tool.tags.filter(tag => !tagList.includes(tag));
|
||
if (tool.tags.length === 0) delete tool.tags;
|
||
}
|
||
});
|
||
showMessage(`Removed tags from ${selectedTools.size} tools`);
|
||
renderBulkGrid();
|
||
}
|
||
}
|
||
|
||
function bulkReplaceTags() {
|
||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
||
const tags = prompt('Enter new tags (comma-separated, will replace all existing tags):');
|
||
if (tags !== null) {
|
||
const tagList = tags.split(',').map(t => t.trim()).filter(t => t);
|
||
selectedTools.forEach(index => {
|
||
const tool = yamlData.tools[index];
|
||
if (tagList.length > 0) {
|
||
tool.tags = tagList;
|
||
} else {
|
||
delete tool.tags;
|
||
}
|
||
});
|
||
showMessage(`Replaced tags for ${selectedTools.size} tools`);
|
||
renderBulkGrid();
|
||
}
|
||
}
|
||
|
||
// NEW: Clear field functions
|
||
function bulkClearTags() {
|
||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
||
if (confirm(`Are you sure you want to clear ALL tags from ${selectedTools.size} selected tools?`)) {
|
||
selectedTools.forEach(index => {
|
||
delete yamlData.tools[index].tags;
|
||
});
|
||
showMessage(`Cleared tags from ${selectedTools.size} tools`);
|
||
renderBulkGrid();
|
||
}
|
||
}
|
||
|
||
function bulkAddDomains() {
|
||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
||
const domains = prompt('Enter domain IDs to add (comma-separated):');
|
||
if (domains) {
|
||
const domainList = domains.split(',').map(d => d.trim()).filter(d => d);
|
||
selectedTools.forEach(index => {
|
||
const tool = yamlData.tools[index];
|
||
tool.domains = [...new Set([...(tool.domains || []), ...domainList])];
|
||
});
|
||
showMessage(`Added domains to ${selectedTools.size} tools`);
|
||
renderBulkGrid();
|
||
}
|
||
}
|
||
|
||
function bulkRemoveDomains() {
|
||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
||
const domains = prompt('Enter domain IDs to remove (comma-separated):');
|
||
if (domains) {
|
||
const domainList = domains.split(',').map(d => d.trim()).filter(d => d);
|
||
selectedTools.forEach(index => {
|
||
const tool = yamlData.tools[index];
|
||
if (tool.domains) {
|
||
tool.domains = tool.domains.filter(domain => !domainList.includes(domain));
|
||
if (tool.domains.length === 0) delete tool.domains;
|
||
}
|
||
});
|
||
showMessage(`Removed domains from ${selectedTools.size} tools`);
|
||
renderBulkGrid();
|
||
}
|
||
}
|
||
|
||
function bulkClearDomains() {
|
||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
||
if (confirm(`Are you sure you want to clear ALL domains from ${selectedTools.size} selected tools?`)) {
|
||
selectedTools.forEach(index => {
|
||
delete yamlData.tools[index].domains;
|
||
});
|
||
showMessage(`Cleared domains from ${selectedTools.size} tools`);
|
||
renderBulkGrid();
|
||
}
|
||
}
|
||
|
||
function bulkAddPhases() {
|
||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
||
const phases = prompt('Enter phase IDs to add (comma-separated):');
|
||
if (phases) {
|
||
const phaseList = phases.split(',').map(p => p.trim()).filter(p => p);
|
||
selectedTools.forEach(index => {
|
||
const tool = yamlData.tools[index];
|
||
tool.phases = [...new Set([...(tool.phases || []), ...phaseList])];
|
||
});
|
||
showMessage(`Added phases to ${selectedTools.size} tools`);
|
||
renderBulkGrid();
|
||
}
|
||
}
|
||
|
||
function bulkRemovePhases() {
|
||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
||
const phases = prompt('Enter phase IDs to remove (comma-separated):');
|
||
if (phases) {
|
||
const phaseList = phases.split(',').map(p => p.trim()).filter(p => p);
|
||
selectedTools.forEach(index => {
|
||
const tool = yamlData.tools[index];
|
||
if (tool.phases) {
|
||
tool.phases = tool.phases.filter(phase => !phaseList.includes(phase));
|
||
if (tool.phases.length === 0) delete tool.phases;
|
||
}
|
||
});
|
||
showMessage(`Removed phases from ${selectedTools.size} tools`);
|
||
renderBulkGrid();
|
||
}
|
||
}
|
||
|
||
function bulkClearPhases() {
|
||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
||
if (confirm(`Are you sure you want to clear ALL phases from ${selectedTools.size} selected tools?`)) {
|
||
selectedTools.forEach(index => {
|
||
delete yamlData.tools[index].phases;
|
||
});
|
||
showMessage(`Cleared phases from ${selectedTools.size} tools`);
|
||
renderBulkGrid();
|
||
}
|
||
}
|
||
|
||
// NEW: Platform operations
|
||
function bulkAddPlatforms() {
|
||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
||
const platforms = prompt('Enter platforms to add (comma-separated, e.g., Windows,Linux,macOS):');
|
||
if (platforms) {
|
||
const platformList = platforms.split(',').map(p => p.trim()).filter(p => p);
|
||
selectedTools.forEach(index => {
|
||
const tool = yamlData.tools[index];
|
||
tool.platforms = [...new Set([...(tool.platforms || []), ...platformList])];
|
||
});
|
||
showMessage(`Added platforms to ${selectedTools.size} tools`);
|
||
renderBulkGrid();
|
||
}
|
||
}
|
||
|
||
function bulkRemovePlatforms() {
|
||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
||
const platforms = prompt('Enter platforms to remove (comma-separated):');
|
||
if (platforms) {
|
||
const platformList = platforms.split(',').map(p => p.trim()).filter(p => p);
|
||
selectedTools.forEach(index => {
|
||
const tool = yamlData.tools[index];
|
||
if (tool.platforms) {
|
||
tool.platforms = tool.platforms.filter(platform => !platformList.includes(platform));
|
||
if (tool.platforms.length === 0) delete tool.platforms;
|
||
}
|
||
});
|
||
showMessage(`Removed platforms from ${selectedTools.size} tools`);
|
||
renderBulkGrid();
|
||
}
|
||
}
|
||
|
||
function bulkClearPlatforms() {
|
||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
||
if (confirm(`Are you sure you want to clear ALL platforms from ${selectedTools.size} selected tools?`)) {
|
||
selectedTools.forEach(index => {
|
||
delete yamlData.tools[index].platforms;
|
||
});
|
||
showMessage(`Cleared platforms from ${selectedTools.size} tools`);
|
||
renderBulkGrid();
|
||
}
|
||
}
|
||
|
||
function bulkAddRelatedConcepts() {
|
||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
||
const concepts = prompt('Enter related concept names to add (comma-separated):');
|
||
if (concepts) {
|
||
const conceptList = concepts.split(',').map(c => c.trim()).filter(c => c);
|
||
selectedTools.forEach(index => {
|
||
const tool = yamlData.tools[index];
|
||
tool.related_concepts = [...new Set([...(tool.related_concepts || []), ...conceptList])];
|
||
});
|
||
showMessage(`Added related concepts to ${selectedTools.size} tools`);
|
||
renderBulkGrid();
|
||
}
|
||
}
|
||
|
||
function bulkRemoveRelatedConcepts() {
|
||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
||
const concepts = prompt('Enter related concept names to remove (comma-separated):');
|
||
if (concepts) {
|
||
const conceptList = concepts.split(',').map(c => c.trim()).filter(c => c);
|
||
selectedTools.forEach(index => {
|
||
const tool = yamlData.tools[index];
|
||
if (tool.related_concepts) {
|
||
tool.related_concepts = tool.related_concepts.filter(concept => !conceptList.includes(concept));
|
||
if (tool.related_concepts.length === 0) delete tool.related_concepts;
|
||
}
|
||
});
|
||
showMessage(`Removed related concepts from ${selectedTools.size} tools`);
|
||
renderBulkGrid();
|
||
}
|
||
}
|
||
|
||
function bulkClearRelatedConcepts() {
|
||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
||
if (confirm(`Are you sure you want to clear ALL related concepts from ${selectedTools.size} selected tools?`)) {
|
||
selectedTools.forEach(index => {
|
||
delete yamlData.tools[index].related_concepts;
|
||
});
|
||
showMessage(`Cleared related concepts from ${selectedTools.size} tools`);
|
||
renderBulkGrid();
|
||
}
|
||
}
|
||
|
||
function bulkDelete() {
|
||
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
|
||
if (confirm(`Are you sure you want to delete ${selectedTools.size} selected tools?`)) {
|
||
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 - Enhanced for CC24-Hub format
|
||
function updateKnowledgeToolSelect() {
|
||
const select = document.getElementById('knowledgeToolSelect');
|
||
select.innerHTML = '<option value="">Choose a tool or concept...</option>';
|
||
|
||
if (!yamlData?.tools) return;
|
||
|
||
yamlData.tools.forEach((tool, index) => {
|
||
const option = document.createElement('option');
|
||
option.value = index;
|
||
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 = createCC24MarkdownTemplate(tool);
|
||
|
||
document.getElementById('markdownContent').value = template;
|
||
document.getElementById('knowledgePreview').classList.remove('hidden');
|
||
}
|
||
|
||
function createCC24MarkdownTemplate(tool) {
|
||
const toolSlug = tool.name.toLowerCase()
|
||
.replace(/[^a-z0-9\s-]/g, '')
|
||
.replace(/\s+/g, '-')
|
||
.replace(/-+/g, '-')
|
||
.replace(/^-|-$/g, '');
|
||
|
||
return `---
|
||
title: "${tool.name}"
|
||
tool_name: "${tool.name}"
|
||
description: "${tool.description.split('\n')[0].trim()}"
|
||
last_updated: ${new Date().toISOString().split('T')[0]}
|
||
author: "CC24-Team"
|
||
difficulty: "${tool.skillLevel || 'intermediate'}"
|
||
categories: ${tool.type === 'concept' ? '["concepts"]' : tool.type === 'method' ? '["methods"]' : '["tools"]'}
|
||
tags: ${JSON.stringify(tool.tags || [])}
|
||
sections:
|
||
overview: true
|
||
installation: ${tool.type === 'software' ? 'true' : 'false'}
|
||
configuration: ${tool.type === 'software' ? 'true' : 'false'}
|
||
usage_examples: true
|
||
best_practices: true
|
||
troubleshooting: ${tool.type === 'software' ? 'true' : 'false'}
|
||
advanced_topics: false
|
||
review_status: "draft"
|
||
---
|
||
|
||
# ${tool.icon ? tool.icon + ' ' : ''}${tool.name}
|
||
|
||
## Übersicht
|
||
|
||
${tool.description}
|
||
|
||
**Typ**: ${tool.type || 'software'}
|
||
**Skill Level**: ${tool.skillLevel || 'intermediate'}
|
||
**Offizielle URL**: [${tool.name}](${tool.url})
|
||
|
||
${tool.license ? `**Lizenz**: ${tool.license}` : ''}
|
||
${tool.platforms && tool.platforms.length > 0 ? `**Plattformen**: ${tool.platforms.join(', ')}` : ''}
|
||
${tool.accessType ? `**Zugriff**: ${tool.accessType}` : ''}
|
||
|
||
${tool.domains && tool.domains.length > 0 ? `## Forensische Domänen
|
||
|
||
${tool.domains.map(domain => `- ${domain}`).join('\n')}` : ''}
|
||
|
||
${tool.phases && tool.phases.length > 0 ? `## Ermittlungsphasen
|
||
|
||
${tool.phases.map(phase => `- ${phase}`).join('\n')}` : ''}
|
||
|
||
${tool.type === 'concept' ? `## Grundlagen
|
||
|
||
### Kernkonzepte
|
||
|
||
- Konzept 1: Beschreibung
|
||
- Konzept 2: Beschreibung
|
||
- Konzept 3: Beschreibung
|
||
|
||
### Anwendungsbereiche
|
||
|
||
Dieses Konzept findet Anwendung in:
|
||
- Anwendungsbereich 1
|
||
- Anwendungsbereich 2
|
||
- Anwendungsbereich 3
|
||
|
||
### Verwandte Konzepte
|
||
|
||
${tool.related_concepts ? tool.related_concepts.map(c => `- ${c}`).join('\n') : '- Verwandtes Konzept 1\n- Verwandtes Konzept 2'}` : tool.type === 'method' ? `## Methodik
|
||
|
||
### Vorgehensweise
|
||
|
||
1. Schritt 1: Beschreibung
|
||
2. Schritt 2: Beschreibung
|
||
3. Schritt 3: Beschreibung
|
||
|
||
### Voraussetzungen
|
||
|
||
- Voraussetzung 1
|
||
- Voraussetzung 2
|
||
- Voraussetzung 3
|
||
|
||
### Tools für diese Methode
|
||
|
||
- Tool 1
|
||
- Tool 2
|
||
- Tool 3` : `## Installation & Setup
|
||
|
||
### Systemvoraussetzungen
|
||
|
||
- Voraussetzung 1
|
||
- Voraussetzung 2
|
||
- Voraussetzung 3
|
||
|
||
### Installation
|
||
|
||
#### Windows
|
||
\`\`\`bash
|
||
# Installationsschritte für Windows
|
||
\`\`\`
|
||
|
||
#### Linux
|
||
\`\`\`bash
|
||
# Installationsschritte für Linux
|
||
\`\`\`
|
||
|
||
#### macOS
|
||
\`\`\`bash
|
||
# Installationsschritte für macOS
|
||
\`\`\`
|
||
|
||
## Konfiguration
|
||
|
||
### Grundkonfiguration
|
||
|
||
1. Konfigurationsschritt 1
|
||
2. Konfigurationsschritt 2
|
||
3. Konfigurationsschritt 3
|
||
|
||
### Erweiterte Konfiguration
|
||
|
||
- Erweiterte Einstellung 1
|
||
- Erweiterte Einstellung 2
|
||
|
||
## Grundlegende Nutzung
|
||
|
||
### Erste Schritte
|
||
|
||
1. Grundschritt 1
|
||
2. Grundschritt 2
|
||
3. Grundschritt 3
|
||
|
||
### Häufige Befehle
|
||
|
||
\`\`\`bash
|
||
# Befehl 1
|
||
command --option value
|
||
|
||
# Befehl 2
|
||
command2 --help
|
||
\`\`\`
|
||
|
||
### Workflow-Beispiele
|
||
|
||
#### Einfacher Workflow
|
||
1. Workflow-Schritt 1
|
||
2. Workflow-Schritt 2
|
||
3. Workflow-Schritt 3
|
||
|
||
#### Erweiterer Workflow
|
||
1. Erweiterer Schritt 1
|
||
2. Erweiterer Schritt 2
|
||
3. Erweiterer Schritt 3`}
|
||
|
||
## Best Practices
|
||
|
||
### Empfohlene Vorgehensweise
|
||
|
||
- Best Practice 1
|
||
- Best Practice 2
|
||
- Best Practice 3
|
||
|
||
### Häufige Fehler vermeiden
|
||
|
||
- Fehler 1: Lösung
|
||
- Fehler 2: Lösung
|
||
- Fehler 3: Lösung
|
||
|
||
${tool.type === 'software' ? `## Troubleshooting
|
||
|
||
### Häufige Probleme
|
||
|
||
**Problem 1**: Beschreibung
|
||
- Lösung: Lösungsbeschreibung
|
||
|
||
**Problem 2**: Beschreibung
|
||
- Lösung: Lösungsbeschreibung
|
||
|
||
**Problem 3**: Beschreibung
|
||
- Lösung: Lösungsbeschreibung
|
||
|
||
### Log-Analyse
|
||
|
||
Wichtige Log-Dateien und deren Auswertung:
|
||
- Log 1: Pfad und Bedeutung
|
||
- Log 2: Pfad und Bedeutung` : ''}
|
||
|
||
## Erweiterte Themen
|
||
|
||
### Integration mit anderen Tools
|
||
|
||
${tool.related_concepts ? tool.related_concepts.map(c => `- Integration mit ${c}`).join('\n') : '- Integration 1\n- Integration 2'}
|
||
|
||
### Automatisierung
|
||
|
||
- Automatisierungsansatz 1
|
||
- Automatisierungsansatz 2
|
||
|
||
## Ressourcen
|
||
|
||
- [Offizielle Dokumentation](${tool.url})
|
||
${tool.projectUrl ? `- [CC24 Server Zugang](${tool.projectUrl})` : ''}
|
||
- Zusätzliche Ressource 1
|
||
- Zusätzliche Ressource 2
|
||
|
||
## Tags
|
||
|
||
${tool.tags ? tool.tags.map(tag => `\`${tag}\``).join(' ') : '`tag1` `tag2` `tag3`'}
|
||
|
||
---
|
||
|
||
*Zuletzt aktualisiert: ${new Date().toISOString().split('T')[0]}*
|
||
*Autor: CC24-Team*
|
||
`;
|
||
}
|
||
|
||
function downloadMarkdown() {
|
||
const content = document.getElementById('markdownContent').value;
|
||
const select = document.getElementById('knowledgeToolSelect');
|
||
const index = parseInt(select.value);
|
||
const tool = yamlData.tools[index];
|
||
|
||
const toolSlug = tool.name.toLowerCase()
|
||
.replace(/[^a-z0-9\s-]/g, '')
|
||
.replace(/\s+/g, '-')
|
||
.replace(/-+/g, '-')
|
||
.replace(/^-|-$/g, '');
|
||
|
||
const blob = new Blob([content], { type: 'text/markdown' });
|
||
const url = URL.createObjectURL(blob);
|
||
|
||
const a = document.createElement('a');
|
||
a.href = url;
|
||
a.download = `${toolSlug}.md`;
|
||
document.body.appendChild(a);
|
||
a.click();
|
||
document.body.removeChild(a);
|
||
URL.revokeObjectURL(url);
|
||
|
||
showMessage('Markdown template downloaded successfully!');
|
||
}
|
||
|
||
function copyMarkdown() {
|
||
const content = document.getElementById('markdownContent').value;
|
||
navigator.clipboard.writeText(content).then(() => {
|
||
showMessage('Markdown copied to clipboard!');
|
||
}).catch(() => {
|
||
showMessage('Failed to copy to clipboard', 'error');
|
||
});
|
||
}
|
||
|
||
// Validation and Export
|
||
function validateYAML() {
|
||
if (!yamlData) return showMessage('No data to validate', 'error');
|
||
|
||
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
|
||
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.url) validationResults.push(`❌ Tool ${index + 1}: Missing url`);
|
||
if (tool.type && !['software', 'method', 'concept'].includes(tool.type)) {
|
||
validationResults.push(`❌ Tool ${index + 1}: Invalid type`);
|
||
}
|
||
|
||
// Type-specific validation
|
||
if (tool.type === 'software' && (!tool.platforms || tool.platforms.length === 0)) {
|
||
validationResults.push(`⚠️ Tool ${index + 1}: Software should have platforms`);
|
||
}
|
||
|
||
if (tool.type === 'concept' && tool.platforms?.length > 0) {
|
||
validationResults.push(`⚠️ Tool ${index + 1}: Concepts should not have platforms`);
|
||
}
|
||
});
|
||
|
||
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) return showMessage('No data to preview', 'error');
|
||
|
||
const yamlString = jsyaml.dump(yamlData, { indent: 2 });
|
||
document.getElementById('yamlPreviewText').value = yamlString;
|
||
document.getElementById('yamlPreview').classList.remove('hidden');
|
||
}
|
||
|
||
function exportYAML() {
|
||
if (!yamlData) return showMessage('No data to export', 'error');
|
||
|
||
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 = 'tools.yaml';
|
||
document.body.appendChild(a);
|
||
a.click();
|
||
document.body.removeChild(a);
|
||
URL.revokeObjectURL(url);
|
||
|
||
showMessage('YAML file exported successfully!');
|
||
}
|
||
|
||
// Initialize
|
||
init();
|
||
</script>
|
||
</body>
|
||
</html> |