diff --git a/src/components/ToolCard.astro b/src/components/ToolCard.astro
index e4bf10a..e121f7c 100644
--- a/src/components/ToolCard.astro
+++ b/src/components/ToolCard.astro
@@ -9,6 +9,7 @@ export interface Props {
skillLevel: string;
accessType: string;
url: string;
+ projectUrl?: string;
license: string;
tags: string[];
isHosted: boolean;
@@ -20,6 +21,12 @@ const { tool } = Astro.props;
// Determine card styling
const cardClass = tool.isHosted ? 'card card-hosted' : (tool.license !== 'Proprietary' ? 'card card-oss' : 'card');
+
+// Check if tool has a valid project URL for hosted services
+const hasValidProjectUrl = tool.projectUrl !== undefined &&
+ tool.projectUrl !== null &&
+ tool.projectUrl !== "" &&
+ tool.projectUrl.trim() !== "";
---
@@ -74,7 +81,26 @@ const cardClass = tool.isHosted ? 'card card-hosted' : (tool.license !== 'Propri
))}
-
- {tool.isHosted ? 'Access Service' : 'Visit Website'}
-
+
+ {tool.isHosted && hasValidProjectUrl ? (
+
+
+ ) : tool.isHosted ? (
+
+
+ Project Page
+
+ ) : (
+
+
+ Visit Website
+
+ )}
\ No newline at end of file
diff --git a/src/components/ToolMatrix.astro b/src/components/ToolMatrix.astro
index ada2984..b2e2c2f 100644
--- a/src/components/ToolMatrix.astro
+++ b/src/components/ToolMatrix.astro
@@ -12,12 +12,21 @@ const domains = data.domains;
const phases = data.phases;
const tools = data.tools;
-// Create matrix structure
+// Separate collaboration tools from domain-specific tools
+const collaborationTools = tools.filter((tool: any) =>
+ tool.phases.includes('collaboration')
+);
+
+const domainTools = tools.filter((tool: any) =>
+ !tool.phases.includes('collaboration')
+);
+
+// Create matrix structure for domain-specific tools only
const matrix: Record> = {};
domains.forEach((domain: any) => {
matrix[domain.id] = {};
- phases.forEach((phase: any) => {
- matrix[domain.id][phase.id] = tools.filter((tool: any) =>
+ phases.filter((phase: any) => phase.id !== 'collaboration').forEach((phase: any) => {
+ matrix[domain.id][phase.id] = domainTools.filter((tool: any) =>
tool.domains.includes(domain.id) && tool.phases.includes(phase.id)
);
});
@@ -25,36 +34,67 @@ domains.forEach((domain: any) => {
---
-
-
-
- Domain / Phase |
- {phases.map((phase: any) => (
- {phase.name} |
- ))}
-
-
-
- {domains.map((domain: any) => (
+
+
+
+
+
+
DFIR Tools Matrix
+
+
- {domain.name} |
- {phases.map((phase: any) => (
-
- {matrix[domain.id][phase.id].map((tool: any) => (
-
- {tool.name}
-
- ))}
- |
+ Domain / Phase |
+ {phases.filter((phase: any) => phase.id !== 'collaboration').map((phase: any) => (
+ {phase.name} |
))}
- ))}
-
-
+
+
+ {domains.map((domain: any) => (
+
+ {domain.name} |
+ {phases.filter((phase: any) => phase.id !== 'collaboration').map((phase: any) => (
+
+ {matrix[domain.id][phase.id].map((tool: any) => (
+
+ {tool.name}
+
+ ))}
+ |
+ ))}
+
+ ))}
+
+
+
@@ -78,12 +118,11 @@ domains.forEach((domain: any) => {
-
- Visit Website
-
+
+
-
\ No newline at end of file
diff --git a/src/data/tools.yaml b/src/data/tools.yaml
index a75e5e9..df75d2f 100644
--- a/src/data/tools.yaml
+++ b/src/data/tools.yaml
@@ -15,6 +15,7 @@ tools:
skillLevel: "intermediate"
accessType: "download"
url: "https://www.autopsy.com/"
+ projectUrl: ""
license: "Apache 2.0"
tags: ["disk-forensics", "file-recovery", "timeline-analysis"]
isHosted: false
@@ -30,6 +31,7 @@ tools:
skillLevel: "advanced"
accessType: "download"
url: "https://www.volatilityfoundation.org/"
+ projectUrl: ""
license: "VSL"
tags: ["memory-forensics", "malware-analysis", "incident-response"]
isHosted: false
@@ -48,7 +50,8 @@ tools:
platforms: ["Web"]
skillLevel: "intermediate"
accessType: "self-hosted"
- url: "https://thehive.example.lab"
+ url: "https://strangebee.com/"
+ projectUrl: ""
license: "AGPL-3.0"
tags: ["incident-response", "case-management", "collaboration"]
isHosted: true
@@ -66,7 +69,8 @@ tools:
platforms: ["Web"]
skillLevel: "intermediate"
accessType: "self-hosted"
- url: "https://misp.example.lab"
+ url: "https://misp-project.org/"
+ projectUrl: "https://misp.cc24.dev"
license: "AGPL-3.0"
tags: ["threat-intelligence", "ioc-sharing", "collaboration"]
isHosted: true
@@ -83,7 +87,8 @@ tools:
platforms: ["Web"]
skillLevel: "intermediate"
accessType: "self-hosted"
- url: "https://timesketch.example.lab"
+ url: "https://timesketch.org/"
+ projectUrl: "https://timesketch.cc24.dev"
license: "Apache 2.0"
tags: ["timeline-analysis", "collaboration", "visualization"]
isHosted: true
@@ -101,6 +106,7 @@ tools:
skillLevel: "intermediate"
accessType: "download"
url: "https://www.wireshark.org/"
+ projectUrl: ""
license: "GPL-2.0"
tags: ["network-analysis", "pcap", "protocol-analysis"]
isHosted: false
@@ -119,6 +125,7 @@ tools:
skillLevel: "advanced"
accessType: "commercial"
url: "https://www.opentext.com/products/encase-forensic"
+ projectUrl: ""
license: "Proprietary"
tags: ["commercial", "enterprise", "court-approved"]
isHosted: false
@@ -135,6 +142,7 @@ tools:
skillLevel: "advanced"
accessType: "self-hosted"
url: "https://cuckoosandbox.org/"
+ projectUrl: ""
license: "GPL-3.0"
tags: ["malware-analysis", "sandbox", "dynamic-analysis"]
isHosted: true
@@ -151,6 +159,7 @@ tools:
skillLevel: "intermediate"
accessType: "download"
url: "https://exterro.com/ftk-imager"
+ projectUrl: ""
license: "Proprietary"
tags: ["disk-imaging", "preview", "data-acquisition"]
isHosted: false
@@ -167,6 +176,7 @@ tools:
skillLevel: "advanced"
accessType: "self-hosted"
url: "https://github.com/google/grr"
+ projectUrl: ""
license: "Apache 2.0"
tags: ["live-forensics", "remote-response", "dfir"]
isHosted: true
@@ -183,6 +193,7 @@ tools:
skillLevel: "intermediate"
accessType: "download"
url: "https://plaso.readthedocs.io/"
+ projectUrl: ""
license: "Apache 2.0"
tags: ["timeline-analysis", "log-parsing", "dfir"]
isHosted: false
@@ -198,6 +209,7 @@ tools:
skillLevel: "intermediate"
accessType: "download"
url: "https://www.netresec.com/?page=NetworkMiner"
+ projectUrl: ""
license: "Freeware/Commercial"
tags: ["pcap-analysis", "passive-sniffing", "credential-recovery"]
isHosted: false
@@ -214,6 +226,7 @@ tools:
skillLevel: "intermediate"
accessType: "download"
url: "https://www.mandiant.com/resources/download/redline"
+ projectUrl: ""
license: "Proprietary"
tags: ["memory-analysis", "ioc-scan", "host-analysis"]
isHosted: false
@@ -230,6 +243,7 @@ tools:
skillLevel: "intermediate"
accessType: "download"
url: "https://www.kroll.com/en/services/cyber-risk/incident-response-litigation-support/kroll-artifact-parser-extractor-kape"
+ projectUrl: ""
license: "Freeware"
tags: ["triage", "artifact-collection", "parsing"]
isHosted: false
@@ -246,6 +260,7 @@ tools:
skillLevel: "advanced"
accessType: "self-hosted"
url: "https://www.velociraptor.app/"
+ projectUrl: ""
license: "Apache 2.0"
tags: ["dfir", "hunting", "endpoint-monitoring"]
isHosted: true
@@ -262,6 +277,7 @@ tools:
skillLevel: "advanced"
accessType: "self-hosted"
url: "https://arkime.com/"
+ projectUrl: ""
license: "Apache 2.0"
tags: ["packet-capture", "full-packet-analysis", "network-forensics"]
isHosted: true
@@ -279,10 +295,43 @@ tools:
skillLevel: "advanced"
accessType: "commercial"
url: "https://www.x-ways.net/forensics/"
+ projectUrl: ""
license: "Proprietary"
tags: ["disk-forensics", "file-recovery", "commercial"]
isHosted: false
+ # Collaboration Tools - Domain-agnostic
+ - name: "Nextcloud"
+ description: "Self-hosted file sharing and collaboration platform for secure data exchange"
+ domains: [] # Domain-agnostic
+ phases:
+ - "collaboration"
+ platforms: ["Web"]
+ skillLevel: "beginner"
+ accessType: "self-hosted"
+ url: "https://nextcloud.com/de/"
+ projectUrl: "https://cloud.cc24.dev"
+ license: "AGPL-3.0"
+ tags: ["file-sharing", "collaboration", "document-management", "secure-storage"]
+ isHosted: true
+ statusUrl: "https://uptime.example.lab/api/badge/10/status"
+
+ - name: "Gitea"
+ description: "Lightweight self-hosted Git service for code collaboration and version control"
+ domains: [] # Domain-agnostic
+ phases:
+ - "collaboration"
+ platforms: ["Web"]
+ skillLevel: "intermediate"
+ accessType: "self-hosted"
+ url: "https://git.example.lab"
+ projectUrl: ""
+ license: "MIT"
+ tags: ["version-control", "git", "code-collaboration", "documentation"]
+ isHosted: true
+ statusUrl: "https://uptime.example.lab/api/badge/11/status"
+
+
# Domain definitions for reference
domains:
- id: "storage-file-system"
@@ -309,4 +358,6 @@ phases:
- id: "analysis"
name: "Analysis"
- id: "reporting"
- name: "Reporting"
\ No newline at end of file
+ name: "Reporting"
+ - id: "collaboration"
+ name: "General Tools for Collaboration"
\ No newline at end of file
diff --git a/src/pages/index.astro b/src/pages/index.astro
index eac970e..50c81f6 100644
--- a/src/pages/index.astro
+++ b/src/pages/index.astro
@@ -104,68 +104,107 @@ const tools = data.tools;
}
});
- // Create tool card element
- function createToolCard(tool: any): HTMLElement {
- const cardDiv = document.createElement('div');
- const cardClass = tool.isHosted ? 'card card-hosted' : (tool.license !== 'Proprietary' ? 'card card-oss' : 'card');
- cardDiv.className = cardClass;
-
- cardDiv.innerHTML = `
-
-
${tool.name}
-
- ${tool.isHosted ? 'Self-Hosted' : ''}
- ${tool.license !== 'Proprietary' ? 'Open Source' : ''}
-
-
-
-
- ${tool.description}
-
-
-
-
-
-
- ${tool.platforms.join(', ')}
-
-
-
-
-
-
- ${tool.skillLevel}
-
-
-
-
-
-
- ${tool.license}
-
-
-
-
-
- ${tool.tags.map((tag: string) => `${tag}`).join('')}
-
-
-
- ${tool.isHosted ? 'Access Service' : 'Visit Website'}
+// This replaces the createToolCard function in index.astro script section
+
+// This replaces the createToolCard function in index.astro script section
+
+// This replaces the createToolCard function in index.astro script section
+
+// Create tool card element
+function createToolCard(tool) {
+ const cardDiv = document.createElement('div');
+ const cardClass = tool.isHosted ? 'card card-hosted' : (tool.license !== 'Proprietary' ? 'card card-oss' : 'card');
+ cardDiv.className = cardClass;
+
+ // Create button HTML based on hosting status
+ const hasValidProjectUrl = tool.projectUrl !== undefined &&
+ tool.projectUrl !== null &&
+ tool.projectUrl !== "" &&
+ tool.projectUrl.trim() !== "";
+
+ let buttonHTML;
+ if (tool.isHosted && hasValidProjectUrl) {
+ // Two buttons for self-hosted tools with both URLs
+ buttonHTML = `
+
+ `;
+ } else if (tool.isHosted) {
+ // Single button for self-hosted tools with only project URL
+ buttonHTML = `
+
+ Project Page
+
+ `;
+ } else {
+ // Single button for non-hosted tools
+ buttonHTML = `
+
+ Visit Website
+
+ `;
+ }
+
+ cardDiv.innerHTML = `
+
+
${tool.name}
+
+ ${tool.isHosted ? 'Self-Hosted' : ''}
+ ${tool.license !== 'Proprietary' ? 'Open Source' : ''}
+
+
+
+
+ ${tool.description}
+
+
+
+
+
+
+ ${tool.platforms.join(', ')}
+
+
- return cardDiv;
- }
+
+
+
+ ${tool.skillLevel}
+
+
+
+
+
+
+ ${tool.license}
+
+
+
+
+
+ ${tool.tags.map(tag => `${tag}`).join('')}
+
+
+ ${buttonHTML}
+ `;
+
+ return cardDiv;
+}
});
\ No newline at end of file
diff --git a/src/styles/global.css b/src/styles/global.css
index 6414003..264bae8 100644
--- a/src/styles/global.css
+++ b/src/styles/global.css
@@ -472,6 +472,15 @@ footer {
flex-direction: column;
text-align: center;
}
+ /* Add this inside the existing @media (max-width: 768px) block */
+ .collaboration-tools-compact {
+ flex-direction: column;
+ }
+
+ .collaboration-tool-compact {
+ min-width: auto;
+ max-width: none;
+ }
}
/* Animations */
@@ -482,4 +491,64 @@ footer {
.fade-in {
animation: fadeIn 0.3s ease-in;
+}
+
+/* Compact Collaboration Tools for Matrix View */
+.collaboration-tools-compact {
+ display: flex;
+ gap: 1rem;
+ flex-wrap: wrap;
+}
+
+.collaboration-tool-compact {
+ background-color: var(--color-bg);
+ border: 1px solid var(--color-border);
+ border-radius: 0.375rem;
+ padding: 0.75rem;
+ min-width: 200px;
+ max-width: 300px;
+ flex: 1;
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+.collaboration-tool-compact:hover {
+ border-color: var(--color-primary);
+ box-shadow: var(--shadow-sm);
+}
+
+.collaboration-tool-compact.hosted {
+ background-color: var(--color-hosted-bg);
+ border-color: var(--color-hosted);
+}
+
+.collaboration-tool-compact.oss {
+ background-color: var(--color-oss-bg);
+ border-color: var(--color-oss);
+}
+
+.tool-compact-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: start;
+ margin-bottom: 0.5rem;
+}
+
+.badge-mini {
+ display: inline-flex;
+ align-items: center;
+ padding: 0.0625rem 0.375rem;
+ border-radius: 9999px;
+ font-size: 0.625rem;
+ font-weight: 500;
+}
+
+.badge-mini.badge-primary {
+ background-color: var(--color-primary);
+ color: white;
+}
+
+.badge-mini.badge-success {
+ background-color: var(--color-accent);
+ color: white;
}
\ No newline at end of file