diff --git a/dfir_yaml_editor.html b/dfir_yaml_editor.html
index b39e8f0..2244239 100644
--- a/dfir_yaml_editor.html
+++ b/dfir_yaml_editor.html
@@ -169,6 +169,12 @@
margin: 2px;
}
+ .tag.domain-agnostic {
+ background: #e8f5e8;
+ color: #27ae60;
+ font-weight: bold;
+ }
+
.skill-badge {
display: inline-block;
padding: 4px 12px;
@@ -182,8 +188,7 @@
.skill-beginner { background: #d5f4e6; color: #27ae60; }
.skill-intermediate { background: #ffeaa7; color: #e17055; }
.skill-advanced { background: #fab1a0; color: #d63031; }
- .skill-expert { background: #fab1a0; color: #3a1b1b38; }
-
+ .skill-expert { background: #2c3e50; color: #ffffff; }
.form-section {
background: #f8f9fa;
@@ -395,8 +400,7 @@
.skill-fill.beginner { background: linear-gradient(90deg, #27ae60, #2ecc71); }
.skill-fill.intermediate { background: linear-gradient(90deg, #f39c12, #e67e22); }
.skill-fill.advanced { background: linear-gradient(90deg, #e74c3c, #c0392b); }
- .skill-fill.expert { background: linear-gradient(90deg, #241513, #422825); }
-
+ .skill-fill.expert { background: linear-gradient(90deg, #2c3e50, #34495e); }
.skill-count {
min-width: 30px;
@@ -404,6 +408,24 @@
font-weight: bold;
color: #2c3e50;
}
+
+ .error-message {
+ background: #f8d7da;
+ color: #721c24;
+ padding: 12px;
+ border-radius: 8px;
+ margin: 10px 0;
+ border: 1px solid #f5c6cb;
+ }
+
+ .success-message {
+ background: #d4edda;
+ color: #155724;
+ padding: 12px;
+ border-radius: 8px;
+ margin: 10px 0;
+ border: 1px solid #c3e6cb;
+ }
@@ -444,6 +466,10 @@
@@ -959,7 +992,7 @@ phases:
${knowledgebaseIndicator}
@@ -995,6 +1028,12 @@ phases:
return card;
}
+ function getDomainAgnosticName(id) {
+ if (!yamlData['domain-agnostic-software']) return id;
+ const category = yamlData['domain-agnostic-software'].find(cat => cat.id === id);
+ return category ? category.name : id;
+ }
+
function filterTools() {
const searchTerm = document.getElementById('searchBar').value.toLowerCase();
const cards = document.querySelectorAll('#toolsGrid .tool-card');
@@ -1004,7 +1043,6 @@ phases:
cards.forEach((card, index) => {
const tool = yamlData.tools[index];
- // Search in multiple fields
const searchableText = [
tool.name || '',
tool.description || '',
@@ -1012,6 +1050,7 @@ phases:
...(tool.domains || []),
...(tool.phases || []),
...(tool.platforms || []),
+ ...(tool['domain-agnostic-software'] || []),
tool.skillLevel || '',
tool.license || '',
tool.accessType || '',
@@ -1030,15 +1069,13 @@ phases:
currentEditingIndex = index;
const tool = yamlData.tools[index];
- // Switch to editor tab
showTab('editor');
document.querySelector('[onclick="showTab(\'editor\')"]').classList.add('active');
- // Update form title
document.getElementById('editorTitle').textContent = `Edit Tool: ${tool.name}`;
document.getElementById('deleteBtn').style.display = 'inline-block';
- // Populate form
+ // Populate form fields
document.getElementById('toolName').value = tool.name || '';
document.getElementById('description').value = tool.description || '';
document.getElementById('skillLevel').value = tool.skillLevel || '';
@@ -1049,31 +1086,23 @@ phases:
document.getElementById('statusUrl').value = tool.statusUrl || '';
document.getElementById('knowledgebase').checked = tool.knowledgebase || false;
- // Set platforms
- const platforms = tool.platforms || [];
- document.querySelectorAll('#platformsCheckbox input').forEach(checkbox => {
- checkbox.checked = platforms.includes(checkbox.value);
- });
+ // Set checkboxes
+ setCheckboxValues('#platformsCheckbox input', tool.platforms || []);
+ setCheckboxValues('#domainsCheckbox input', tool.domains || []);
+ setCheckboxValues('#phasesCheckbox input', tool.phases || []);
+ setCheckboxValues('#domainAgnosticCheckbox input', tool['domain-agnostic-software'] || []);
- // Set domains
- const domains = tool.domains || [];
- document.querySelectorAll('#domainsCheckbox input').forEach(checkbox => {
- checkbox.checked = domains.includes(checkbox.value);
- });
-
- // Set phases
- const phases = tool.phases || [];
- document.querySelectorAll('#phasesCheckbox input').forEach(checkbox => {
- checkbox.checked = phases.includes(checkbox.value);
- });
-
- // Set tags
populateTags(tool.tags || []);
}
+ function setCheckboxValues(selector, values) {
+ document.querySelectorAll(selector).forEach(checkbox => {
+ checkbox.checked = values.includes(checkbox.value);
+ });
+ }
+
function populateTags(tags) {
const container = document.getElementById('tagContainer');
- // Clear existing tags except input
container.querySelectorAll('.removable-tag').forEach(tag => tag.remove());
tags.forEach(tag => {
@@ -1112,47 +1141,58 @@ phases:
}
function saveTool() {
- if (!yamlData) {
- yamlData = { tools: [], domains: [], phases: [] };
- }
- if (!yamlData.tools) {
- yamlData.tools = [];
- }
+ try {
+ if (!yamlData) {
+ yamlData = { tools: [], domains: [], phases: [], 'domain-agnostic-software': [] };
+ }
+ if (!yamlData.tools) {
+ yamlData.tools = [];
+ }
- const tool = {
- name: document.getElementById('toolName').value,
- description: document.getElementById('description').value,
- domains: getCheckedValues('#domainsCheckbox input:checked'),
- phases: getCheckedValues('#phasesCheckbox input:checked'),
- platforms: getCheckedValues('#platformsCheckbox input:checked'),
- skillLevel: document.getElementById('skillLevel').value,
- accessType: document.getElementById('accessType').value,
- url: document.getElementById('url').value,
- projectUrl: document.getElementById('projectUrl').value,
- license: document.getElementById('license').value,
- knowledgebase: document.getElementById('knowledgebase').checked,
- tags: getTags()
- };
+ const tool = {
+ name: document.getElementById('toolName').value,
+ description: document.getElementById('description').value,
+ domains: getCheckedValues('#domainsCheckbox input:checked'),
+ phases: getCheckedValues('#phasesCheckbox input:checked'),
+ platforms: getCheckedValues('#platformsCheckbox input:checked'),
+ 'domain-agnostic-software': getCheckedValues('#domainAgnosticCheckbox input:checked'),
+ skillLevel: document.getElementById('skillLevel').value,
+ accessType: document.getElementById('accessType').value,
+ url: document.getElementById('url').value,
+ projectUrl: document.getElementById('projectUrl').value,
+ license: document.getElementById('license').value,
+ knowledgebase: document.getElementById('knowledgebase').checked,
+ tags: getTags()
+ };
- // Add statusUrl if provided
- const statusUrl = document.getElementById('statusUrl').value;
- if (statusUrl) {
- tool.statusUrl = statusUrl;
+ // 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) {
+ delete tool[key];
+ }
+ });
+
+ const statusUrl = document.getElementById('statusUrl').value;
+ if (statusUrl) {
+ tool.statusUrl = statusUrl;
+ }
+
+ if (currentEditingIndex >= 0) {
+ yamlData.tools[currentEditingIndex] = tool;
+ showMessage('Tool updated successfully!');
+ } else {
+ yamlData.tools.push(tool);
+ showMessage('Tool added successfully!');
+ }
+
+ clearForm();
+ updateStats();
+ renderToolsGrid();
+ } catch (error) {
+ showMessage('Error saving tool: ' + error.message, 'error');
}
-
- if (currentEditingIndex >= 0) {
- // Update existing tool
- yamlData.tools[currentEditingIndex] = tool;
- alert('Tool updated successfully!');
- } else {
- // Add new tool
- yamlData.tools.push(tool);
- alert('Tool added successfully!');
- }
-
- clearForm();
- updateStats();
- renderToolsGrid();
}
function getCheckedValues(selector) {
@@ -1188,7 +1228,7 @@ phases:
clearForm();
updateStats();
renderToolsGrid();
- alert('Tool deleted successfully!');
+ showMessage('Tool deleted successfully!');
}
}
@@ -1223,7 +1263,7 @@ phases:
function bulkUpdateSkillLevel() {
if (selectedTools.size === 0) {
- alert('No tools selected');
+ showMessage('No tools selected', 'error');
return;
}
@@ -1232,66 +1272,83 @@ phases:
selectedTools.forEach(index => {
yamlData.tools[index].skillLevel = skillLevel;
});
- alert(`Updated skill level for ${selectedTools.size} tools`);
+ showMessage(`Updated skill level for ${selectedTools.size} tools`);
renderBulkGrid();
}
}
function bulkUpdateDomains() {
if (selectedTools.size === 0) {
- alert('No tools selected');
+ showMessage('No tools selected', 'error');
return;
}
const domains = prompt('Enter domains (comma-separated):');
if (domains) {
- const domainList = domains.split(',').map(d => d.trim());
+ const domainList = domains.split(',').map(d => d.trim()).filter(d => d);
selectedTools.forEach(index => {
yamlData.tools[index].domains = domainList;
});
- alert(`Updated domains for ${selectedTools.size} tools`);
+ showMessage(`Updated domains for ${selectedTools.size} tools`);
renderBulkGrid();
}
}
function bulkUpdatePhases() {
if (selectedTools.size === 0) {
- alert('No tools selected');
+ showMessage('No tools selected', 'error');
return;
}
const phases = prompt('Enter phases (comma-separated):');
if (phases) {
- const phaseList = phases.split(',').map(p => p.trim());
+ const phaseList = phases.split(',').map(p => p.trim()).filter(p => p);
selectedTools.forEach(index => {
yamlData.tools[index].phases = phaseList;
});
- alert(`Updated phases for ${selectedTools.size} tools`);
+ showMessage(`Updated phases for ${selectedTools.size} tools`);
+ renderBulkGrid();
+ }
+ }
+
+ function bulkUpdateDomainAgnostic() {
+ if (selectedTools.size === 0) {
+ showMessage('No tools selected', 'error');
+ return;
+ }
+
+ const categories = prompt('Enter domain-agnostic categories (comma-separated):');
+ if (categories) {
+ const categoryList = categories.split(',').map(c => c.trim()).filter(c => c);
+ selectedTools.forEach(index => {
+ yamlData.tools[index]['domain-agnostic-software'] = categoryList;
+ });
+ showMessage(`Updated domain-agnostic categories for ${selectedTools.size} tools`);
renderBulkGrid();
}
}
function bulkUpdateTags() {
if (selectedTools.size === 0) {
- alert('No tools selected');
+ showMessage('No tools selected', 'error');
return;
}
const tags = prompt('Enter tags (comma-separated):');
if (tags) {
- const tagList = tags.split(',').map(t => t.trim());
+ const tagList = tags.split(',').map(t => t.trim()).filter(t => t);
selectedTools.forEach(index => {
yamlData.tools[index].tags = tagList;
});
- alert(`Updated tags for ${selectedTools.size} tools`);
- updateStats(); // Refresh tag analytics
+ showMessage(`Updated tags for ${selectedTools.size} tools`);
+ updateStats();
renderBulkGrid();
}
}
function bulkSetKnowledgebase(value) {
if (selectedTools.size === 0) {
- alert('No tools selected');
+ showMessage('No tools selected', 'error');
return;
}
@@ -1307,26 +1364,27 @@ phases:
});
const actionCompleted = value ? 'marked as knowledgebase' : 'removed knowledgebase flag from';
- alert(`Successfully ${actionCompleted} ${selectedTools.size} tools`);
+ showMessage(`Successfully ${actionCompleted} ${selectedTools.size} tools`);
- updateStats(); // Refresh knowledgebase count
+ updateStats();
renderBulkGrid();
}
function bulkClearField(fieldName) {
if (selectedTools.size === 0) {
- alert('No tools selected');
+ showMessage('No tools selected', 'error');
return;
}
- const fieldDisplayName = fieldName.charAt(0).toUpperCase() + fieldName.slice(1);
+ const fieldDisplayName = fieldName.charAt(0).toUpperCase() + fieldName.slice(1).replace('-', ' ');
if (!confirm(`Are you sure you want to clear ${fieldDisplayName} for ${selectedTools.size} selected tools?`)) {
return;
}
selectedTools.forEach(index => {
if (yamlData.tools[index]) {
- if (fieldName === 'tags' || fieldName === 'domains' || fieldName === 'phases' || fieldName === 'platforms') {
+ const arrayFields = ['tags', 'domains', 'phases', 'platforms', 'domain-agnostic-software'];
+ if (arrayFields.includes(fieldName)) {
yamlData.tools[index][fieldName] = [];
} else {
yamlData.tools[index][fieldName] = '';
@@ -1334,10 +1392,10 @@ phases:
}
});
- alert(`Cleared ${fieldDisplayName} for ${selectedTools.size} tools`);
+ showMessage(`Cleared ${fieldDisplayName} for ${selectedTools.size} tools`);
if (fieldName === 'tags') {
- updateStats(); // Refresh tag analytics if tags were cleared
+ updateStats();
}
renderBulkGrid();
@@ -1345,7 +1403,7 @@ phases:
function bulkDelete() {
if (selectedTools.size === 0) {
- alert('No tools selected');
+ showMessage('No tools selected', 'error');
return;
}
@@ -1357,13 +1415,45 @@ phases:
selectedTools.clear();
updateStats();
renderBulkGrid();
- alert('Tools deleted successfully!');
+ showMessage('Tools deleted successfully!');
}
}
+ function validateYAML() {
+ if (!yamlData) {
+ showMessage('No data to validate', 'error');
+ return;
+ }
+
+ const validationResults = [];
+
+ // Check required sections
+ if (!yamlData.tools) validationResults.push('❌ Missing tools section');
+ if (!yamlData.domains) validationResults.push('❌ Missing domains section');
+ if (!yamlData.phases) validationResults.push('❌ Missing phases section');
+
+ // Validate tools
+ if (yamlData.tools) {
+ yamlData.tools.forEach((tool, index) => {
+ if (!tool.name) validationResults.push(`❌ Tool ${index + 1}: Missing name`);
+ if (!tool.description) validationResults.push(`❌ Tool ${index + 1}: Missing description`);
+ if (!tool.skillLevel) validationResults.push(`❌ Tool ${index + 1}: Missing skillLevel`);
+ });
+ }
+
+ const container = document.getElementById('validationContent');
+ if (validationResults.length === 0) {
+ container.innerHTML = '
✅ YAML structure is valid!
';
+ } else {
+ container.innerHTML = '
' + validationResults.join('
') + '
';
+ }
+
+ document.getElementById('validationResults').classList.remove('hidden');
+ }
+
function previewYAML() {
if (!yamlData) {
- alert('No data to preview');
+ showMessage('No data to preview', 'error');
return;
}
@@ -1374,7 +1464,7 @@ phases:
function exportYAML() {
if (!yamlData) {
- alert('No data to export');
+ showMessage('No data to export', 'error');
return;
}
@@ -1389,6 +1479,8 @@ phases:
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
+
+ showMessage('YAML file exported successfully!');
}
diff --git a/src/components/AIQueryInterface.astro b/src/components/AIQueryInterface.astro
index e58399a..d70b6f5 100644
--- a/src/components/AIQueryInterface.astro
+++ b/src/components/AIQueryInterface.astro
@@ -9,6 +9,7 @@ const yamlContent = await fs.readFile(yamlPath, 'utf8');
const data = load(yamlContent) as any;
const tools = data.tools;
const phases = data.phases;
+const domainAgnosticSoftware = data['domain-agnostic-software'] || []; // Add this line
---
@@ -88,7 +89,7 @@ const phases = data.phases;
-