introduce concepts phase 1-2

This commit is contained in:
overcuriousity 2025-07-20 17:33:56 +02:00
parent 0648647c8a
commit 202ee5f801
7 changed files with 200 additions and 85 deletions

View File

@ -21,8 +21,10 @@ export interface Props {
const { tool } = Astro.props; const { tool } = Astro.props;
// Check if this is a method vs software
// Check types
const isMethod = tool.type === 'method'; const isMethod = tool.type === 'method';
const isConcept = tool.type === 'concept';
// Check if tool has a valid project URL (means we're hosting it) // Check if tool has a valid project URL (means we're hosting it)
const hasValidProjectUrl = tool.projectUrl !== undefined && const hasValidProjectUrl = tool.projectUrl !== undefined &&
@ -34,7 +36,8 @@ const hasValidProjectUrl = tool.projectUrl !== undefined &&
const hasKnowledgebase = tool.knowledgebase === true; const hasKnowledgebase = tool.knowledgebase === true;
// Determine card styling based on type and hosting status // Determine card styling based on type and hosting status
const cardClass = isMethod ? 'card card-method tool-card' : const cardClass = isConcept ? 'card card-concept tool-card' :
isMethod ? 'card card-method tool-card' :
hasValidProjectUrl ? 'card card-hosted tool-card' : hasValidProjectUrl ? 'card card-hosted tool-card' :
(tool.license !== 'Proprietary' ? 'card card-oss tool-card' : 'card tool-card'); (tool.license !== 'Proprietary' ? 'card card-oss tool-card' : 'card tool-card');
--- ---
@ -99,9 +102,14 @@ const cardClass = isMethod ? 'card card-method tool-card' :
))} ))}
</div> </div>
<!-- Buttons - Fixed at Bottom --> <!-- Buttons - Fixed at Bottom -->
<div class="tool-card-buttons" onclick="event.stopPropagation();"> <div class="tool-card-buttons" onclick="event.stopPropagation();">
{isMethod ? ( {isConcept ? (
<!-- Concept button -->
<a href={tool.url} target="_blank" rel="noopener noreferrer" class="btn btn-primary single-button" style="background-color: var(--color-concept); border-color: var(--color-concept);">
Mehr erfahren
</a>
) : isMethod ? (
<!-- Method button --> <!-- Method button -->
<a href={tool.projectUrl || tool.url} target="_blank" rel="noopener noreferrer" class="btn btn-primary single-button" style="background-color: var(--color-method); border-color: var(--color-method);"> <a href={tool.projectUrl || tool.url} target="_blank" rel="noopener noreferrer" class="btn btn-primary single-button" style="background-color: var(--color-method); border-color: var(--color-method);">
Zur Methode Zur Methode

View File

@ -342,35 +342,6 @@ const sortedTags = Object.entries(tagFrequency)
return true; return true;
}); });
// Sort filtered results: methods first, then self-hosted, then OSS, proprietary last
filtered.sort((a, b) => {
const aMethod = isMethod(a);
const bMethod = isMethod(b);
const aHosted = isToolHosted(a);
const bHosted = isToolHosted(b);
const aProprietary = !aMethod && a.license === 'Proprietary';
const bProprietary = !bMethod && b.license === 'Proprietary';
// Methods first
//if (aMethod && !bMethod) return -1;
//if (!aMethod && bMethod) return 1;
// If both are methods or both are tools
if (aMethod === bMethod) {
// Self-hosted tools first (regardless of license)
if (aHosted && !bHosted) return -1;
if (!aHosted && bHosted) return 1;
// If both have same hosting status, proprietary tools go last
if (aHosted === bHosted) {
if (!aProprietary && bProprietary) return -1;
if (aProprietary && !bProprietary) return 1;
}
}
return 0;
});
// Update matrix highlighting // Update matrix highlighting
updateMatrixHighlighting(); updateMatrixHighlighting();
// Emit custom event with filtered results // Emit custom event with filtered results
@ -417,17 +388,7 @@ const sortedTags = Object.entries(tagFrequency)
window.dispatchEvent(new CustomEvent('viewChanged', { detail: view })); window.dispatchEvent(new CustomEvent('viewChanged', { detail: view }));
if (view === 'hosted') { if (view === 'hosted') {
const hosted = window.toolsData.filter(tool => isToolHosted(tool)); const hosted = window.toolsData.filter(tool => isToolHosted(tool));
hosted.sort((a, b) => {
const aProprietary = a.license === 'Proprietary';
const bProprietary = b.license === 'Proprietary';
if (!aProprietary && bProprietary) return -1;
if (aProprietary && !bProprietary) return 1;
return 0;
});
window.dispatchEvent(new CustomEvent('toolsFiltered', { detail: hosted })); window.dispatchEvent(new CustomEvent('toolsFiltered', { detail: hosted }));
} else { } else {
filterTools(); filterTools();

View File

@ -24,6 +24,7 @@ domains.forEach((domain: any) => {
matrix[domain.id] = {}; matrix[domain.id] = {};
phases.forEach((phase: any) => { phases.forEach((phase: any) => {
matrix[domain.id][phase.id] = tools.filter((tool: any) => matrix[domain.id][phase.id] = tools.filter((tool: any) =>
tool.type !== 'concept' && // Exclude concepts from matrix
tool.domains && tool.domains.includes(domain.id) && tool.domains && tool.domains.includes(domain.id) &&
tool.phases && tool.phases.includes(phase.id) tool.phases && tool.phases.includes(phase.id)
); );
@ -407,6 +408,11 @@ domains.forEach((domain: any) => {
// Re-populate with filtered tools based on domains × phases // Re-populate with filtered tools based on domains × phases
filtered.forEach(tool => { filtered.forEach(tool => {
// Skip concepts - they don't belong in matrix
if (tool.type === 'concept') {
return;
}
const isMethod = tool.type === 'method'; const isMethod = tool.type === 'method';
const hasValidProjectUrl = tool.projectUrl !== undefined && const hasValidProjectUrl = tool.projectUrl !== undefined &&
tool.projectUrl !== null && tool.projectUrl !== null &&

View File

@ -1904,6 +1904,95 @@ tools:
- jamf - jamf
- enterprise - enterprise
- commandline - commandline
- name: Regular Expressions (Regex)
icon: 🔤
type: concept
description: >-
Pattern matching language for searching, extracting, and manipulating text.
Essential for log analysis, malware signature creation, and data extraction from
unstructured sources. Forms the backbone of many forensic tools and custom scripts.
domains:
- incident-response
- malware-analysis
- network-forensics
- fraud-investigation
phases:
- examination
- analysis
platforms: []
domain-agnostic-software: null
skillLevel: intermediate
accessType: null
url: https://regexr.com/
projectUrl: null
license: null
knowledgebase: false
tags:
- pattern-matching
- text-processing
- log-analysis
- string-manipulation
- search-algorithms
- name: SQL Query Fundamentals
icon: 🗃️
type: concept
description: >-
Structured Query Language for database interrogation and analysis. Critical for
examining application databases, SQLite artifacts from mobile devices, and
browser history databases. Enables complex correlation and filtering of large datasets.
domains:
- incident-response
- mobile-forensics
- fraud-investigation
- cloud-forensics
phases:
- examination
- analysis
platforms: []
domain-agnostic-software: null
skillLevel: intermediate
accessType: null
url: https://www.w3schools.com/sql/
projectUrl: null
license: null
knowledgebase: false
tags:
- database-analysis
- query-language
- data-correlation
- mobile-artifacts
- browser-forensics
- name: Hash Functions & Digital Signatures
icon: 🔐
type: concept
description: >-
Cryptographic principles for data integrity verification and authentication.
Fundamental for evidence preservation, malware identification, and establishing
chain of custody. Understanding of MD5, SHA, and digital signature validation.
domains:
- incident-response
- law-enforcement
- malware-analysis
- cloud-forensics
phases:
- data-collection
- examination
platforms: []
domain-agnostic-software: null
skillLevel: advanced
accessType: null
url: https://en.wikipedia.org/wiki/Cryptographic_hash_function
projectUrl: null
license: null
knowledgebase: false
tags:
- cryptography
- data-integrity
- evidence-preservation
- malware-identification
- chain-of-custody
domains: domains:
- id: incident-response - id: incident-response
name: Incident Response & Breach-Untersuchung name: Incident Response & Breach-Untersuchung

View File

@ -112,6 +112,29 @@ const tools = data.tools;
// Initial tools HTML // Initial tools HTML
const initialToolsHTML = toolsContainer.innerHTML; const initialToolsHTML = toolsContainer.innerHTML;
// Simple sorting function - no external imports needed
function sortTools(tools, sortBy = 'default') {
const sorted = [...tools]; // Don't mutate original array
switch (sortBy) {
case 'alphabetical':
return sorted.sort((a, b) => a.name.localeCompare(b.name));
case 'difficulty':
const difficultyOrder = { 'novice': 0, 'beginner': 1, 'intermediate': 2, 'advanced': 3, 'expert': 4 };
return sorted.sort((a, b) =>
(difficultyOrder[a.skillLevel] || 999) - (difficultyOrder[b.skillLevel] || 999)
);
case 'type':
const typeOrder = { 'concept': 0, 'method': 1, 'software': 2 };
return sorted.sort((a, b) =>
(typeOrder[a.type] || 999) - (typeOrder[b.type] || 999)
);
case 'default':
default:
return sorted; // No sorting - embrace the entropy
}
}
// Authentication check function // Authentication check function
async function checkAuthentication() { async function checkAuthentication() {
try { try {
@ -177,7 +200,7 @@ const tools = data.tools;
// Hide filter controls in AI mode - AGGRESSIVE APPROACH // Hide filter controls in AI mode - AGGRESSIVE APPROACH
const domainPhaseContainer = document.querySelector('.domain-phase-container') as HTMLElement; const domainPhaseContainer = document.querySelector('.domain-phase-container') as HTMLElement;
const searchInput = document.getElementById('search-tools') as HTMLElement; const searchInput = document.getElementById('search-input') as HTMLElement;
const tagCloud = document.querySelector('.tag-cloud') as HTMLElement; const tagCloud = document.querySelector('.tag-cloud') as HTMLElement;
// Hide all checkbox wrappers // Hide all checkbox wrappers
const checkboxWrappers = document.querySelectorAll('.checkbox-wrapper'); const checkboxWrappers = document.querySelectorAll('.checkbox-wrapper');
@ -227,7 +250,7 @@ const tools = data.tools;
// Show filter controls in matrix mode // Show filter controls in matrix mode
const domainPhaseContainerMatrix = document.querySelector('.domain-phase-container') as HTMLElement; const domainPhaseContainerMatrix = document.querySelector('.domain-phase-container') as HTMLElement;
const searchInputMatrix = document.getElementById('search-tools') as HTMLElement; const searchInputMatrix = document.getElementById('search-input') as HTMLElement;
const tagCloudMatrix = document.querySelector('.tag-cloud') as HTMLElement; const tagCloudMatrix = document.querySelector('.tag-cloud') as HTMLElement;
const checkboxWrappersMatrix = document.querySelectorAll('.checkbox-wrapper'); const checkboxWrappersMatrix = document.querySelectorAll('.checkbox-wrapper');
const tagHeaderMatrix = document.querySelector('.tag-header') as HTMLElement; const tagHeaderMatrix = document.querySelector('.tag-header') as HTMLElement;
@ -262,7 +285,7 @@ const tools = data.tools;
// Show filter controls in grid mode // Show filter controls in grid mode
const domainPhaseContainerGrid = document.querySelector('.domain-phase-container') as HTMLElement; const domainPhaseContainerGrid = document.querySelector('.domain-phase-container') as HTMLElement;
const searchInputGrid = document.getElementById('search-tools') as HTMLElement; const searchInputGrid = document.getElementById('search-input') as HTMLElement;
const tagCloudGrid = document.querySelector('.tag-cloud') as HTMLElement; const tagCloudGrid = document.querySelector('.tag-cloud') as HTMLElement;
const checkboxWrappersGrid = document.querySelectorAll('.checkbox-wrapper'); const checkboxWrappersGrid = document.querySelectorAll('.checkbox-wrapper');
const tagHeaderGrid = document.querySelector('.tag-header') as HTMLElement; const tagHeaderGrid = document.querySelector('.tag-header') as HTMLElement;
@ -318,8 +341,12 @@ const tools = data.tools;
} else { } else {
noResults.style.display = 'none'; noResults.style.display = 'none';
// Render filtered tools // Apply sorting here - single place for all sorting logic
filtered.forEach((tool: any) => { const currentSortOption = 'default'; // Will be dynamic later
const sortedTools = sortTools(filtered, currentSortOption);
// Render sorted tools
sortedTools.forEach((tool: any) => {
const toolCard = createToolCard(tool); const toolCard = createToolCard(tool);
toolsContainer.appendChild(toolCard); toolsContainer.appendChild(toolCard);
}); });
@ -338,6 +365,7 @@ const tools = data.tools;
function createToolCard(tool) { function createToolCard(tool) {
const isMethod = tool.type === 'method'; const isMethod = tool.type === 'method';
const isConcept = tool.type === 'concept';
const hasValidProjectUrl = tool.projectUrl !== undefined && const hasValidProjectUrl = tool.projectUrl !== undefined &&
tool.projectUrl !== null && tool.projectUrl !== null &&
tool.projectUrl !== "" && tool.projectUrl !== "" &&
@ -346,7 +374,8 @@ function createToolCard(tool) {
const hasKnowledgebase = tool.knowledgebase === true; const hasKnowledgebase = tool.knowledgebase === true;
const cardDiv = document.createElement('div'); const cardDiv = document.createElement('div');
const cardClass = isMethod ? 'card card-method tool-card' : const cardClass = isConcept ? 'card card-concept tool-card' :
isMethod ? 'card card-method tool-card' :
hasValidProjectUrl ? 'card card-hosted tool-card' : hasValidProjectUrl ? 'card card-hosted tool-card' :
(tool.license !== 'Proprietary' ? 'card card-oss tool-card' : 'card tool-card'); (tool.license !== 'Proprietary' ? 'card card-oss tool-card' : 'card tool-card');
cardDiv.className = cardClass; cardDiv.className = cardClass;
@ -358,7 +387,7 @@ function createToolCard(tool) {
<div class="tool-card-header"> <div class="tool-card-header">
<h3>${tool.icon ? `<span style="margin-right: 0.5rem; font-size: 1.125rem;">${tool.icon}</span>` : ''}${tool.name}</h3> <h3>${tool.icon ? `<span style="margin-right: 0.5rem; font-size: 1.125rem;">${tool.icon}</span>` : ''}${tool.name}</h3>
<div class="tool-card-badges"> <div class="tool-card-badges">
${!isMethod && hasValidProjectUrl ? '<span class="badge badge-primary">CC24-Server</span>' : ''} ${!isMethod && !isConcept && hasValidProjectUrl ? '<span class="badge badge-primary">CC24-Server</span>' : ''}
${hasKnowledgebase ? '<span class="badge badge-error">📖</span>' : ''} ${hasKnowledgebase ? '<span class="badge badge-error">📖</span>' : ''}
</div> </div>
</div> </div>
@ -395,7 +424,7 @@ function createToolCard(tool) {
<polyline points="14 2 14 8 20 8"></polyline> <polyline points="14 2 14 8 20 8"></polyline>
</svg> </svg>
<span style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;"> <span style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;">
${isMethod ? 'Methode' : tool.license === 'Proprietary' ? 'Prop.' : tool.license?.split(' ')[0] || 'N/A'} ${isConcept ? 'Konzept' : isMethod ? 'Methode' : tool.license === 'Proprietary' ? 'Prop.' : tool.license?.split(' ')[0] || 'N/A'}
</span> </span>
</div> </div>
</div> </div>
@ -405,7 +434,11 @@ function createToolCard(tool) {
</div> </div>
<div class="tool-card-buttons" onclick="event.stopPropagation();"> <div class="tool-card-buttons" onclick="event.stopPropagation();">
${isMethod ? ` ${isConcept ? `
<a href="${tool.url}" target="_blank" rel="noopener noreferrer" class="btn btn-primary single-button" style="background-color: var(--color-concept); border-color: var(--color-concept);">
Mehr erfahren
</a>
` : isMethod ? `
<a href="${tool.projectUrl || tool.url}" target="_blank" rel="noopener noreferrer" class="btn btn-primary single-button" style="background-color: var(--color-method); border-color: var(--color-method);"> <a href="${tool.projectUrl || tool.url}" target="_blank" rel="noopener noreferrer" class="btn btn-primary single-button" style="background-color: var(--color-method); border-color: var(--color-method);">
Zur Methode Zur Methode
</a> </a>

View File

@ -11,23 +11,28 @@
:root { :root {
/* Light Theme Colors */ /* Light Theme Colors */
--color-bg: #fff; --color-bg: #fff;
--color-bg-secondary: #f5f5f5; --color-bg-secondary: #f8fafc;
--color-bg-tertiary: #e0e0e0; --color-bg-tertiary: #e2e8f0;
--color-text: #1a1a1a; --color-text: #1e293b;
--color-text-secondary: #666; --color-text-secondary: #64748b;
--color-border: #d0d0d0; --color-border: #cbd5e1;
--color-primary: #2563eb; --color-primary: #2563eb;
--color-primary-hover: #1d4ed8; --color-primary-hover: #1d4ed8;
--color-accent: #10b981; --color-accent: #059669;
--color-accent-hover: #059669; --color-accent-hover: #047857;
--color-warning: #f59e0b; --color-warning: #d97706;
--color-error: #ef4444; --color-error: #dc2626;
--color-hosted: #8b5cf6;
/* Enhanced card type colors */
--color-hosted: #7c3aed; /* More vibrant purple */
--color-hosted-bg: #f3f0ff; --color-hosted-bg: #f3f0ff;
--color-oss: #10b981; --color-oss: #059669; /* Deeper green for better contrast */
--color-oss-bg: #ecfdf5; --color-oss-bg: #ecfdf5;
--color-method: #3a90ed; --color-method: #0891b2; /* Distinct teal-cyan for methods */
--color-method-bg: #f3f4f6; --color-method-bg: #f0f9ff; /* Fixed: proper light background */
--color-concept: #ea580c; /* Warmer orange-red for concepts */
--color-concept-bg: #fff7ed;
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 5%); --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 5%);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 10%); --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 10%);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 10%); --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 10%);
@ -39,24 +44,29 @@
[data-theme="dark"] { [data-theme="dark"] {
/* Dark Theme Colors */ /* Dark Theme Colors */
--color-bg: #0f0f0f; --color-bg: #0f172a;
--color-bg-secondary: #1a1a1a; --color-bg-secondary: #1e293b;
--color-bg-tertiary: #262626; --color-bg-tertiary: #334155;
--color-text: #e5e5e5; --color-text: #f1f5f9;
--color-text-secondary: #a3a3a3; --color-text-secondary: #94a3b8;
--color-border: #404040; --color-border: #475569;
--color-primary: #3b82f6; --color-primary: #3b82f6;
--color-primary-hover: #60a5fa; --color-primary-hover: #60a5fa;
--color-accent: #34d399; --color-accent: #10b981;
--color-accent-hover: #6ee7b7; --color-accent-hover: #34d399;
--color-warning: #fbbf24; --color-warning: #f59e0b;
--color-error: #f87171; --color-error: #f87171;
--color-hosted: #a78bfa;
/* Enhanced dark card type colors */
--color-hosted: #a855f7; /* Brighter purple for dark mode */
--color-hosted-bg: #2e1065; --color-hosted-bg: #2e1065;
--color-oss: #34d399; --color-oss: #10b981; /* Vibrant green */
--color-oss-bg: #064e3b; --color-oss-bg: #064e3b;
--color-method: #8bbdfa; --color-method: #0891b2; /* Distinct teal-cyan */
--color-method-bg: #2e4e81; --color-method-bg: #164e63; /* Proper dark background */
--color-concept: #f97316; /* Bright orange for dark mode */
--color-concept-bg: #7c2d12;
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 30%); --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 30%);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 40%); --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 40%);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 50%); --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 50%);
@ -313,6 +323,11 @@ input[type="checkbox"] {
border-color: var(--color-method); border-color: var(--color-method);
} }
.card-concept {
background-color: var(--color-concept-bg);
border-color: var(--color-concept);
}
.grid-auto-fit { .grid-auto-fit {
display: grid; display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 350px)); grid-template-columns: repeat(auto-fill, minmax(300px, 350px));

View File

@ -6,7 +6,7 @@ import { z } from 'zod';
const ToolSchema = z.object({ const ToolSchema = z.object({
name: z.string(), name: z.string(),
icon: z.string().optional().nullable(), icon: z.string().optional().nullable(),
type: z.string(), type: z.enum(['software', 'method', 'concept']), // Make this more explicit
description: z.string(), description: z.string(),
domains: z.array(z.string()).optional().nullable().default([]), domains: z.array(z.string()).optional().nullable().default([]),
phases: z.array(z.string()).optional().nullable().default([]), phases: z.array(z.string()).optional().nullable().default([]),
@ -127,15 +127,18 @@ export async function getToolsData(): Promise<ToolsData> {
return cachedRandomizedData; return cachedRandomizedData;
} }
// Get compressed data for AI (removes projectUrl and statusUrl) // Get compressed data for AI (removes projectUrl and statusUrl, excludes concepts)
export async function getCompressedToolsDataForAI(): Promise<CompressedToolsData> { export async function getCompressedToolsDataForAI(): Promise<CompressedToolsData> {
if (!cachedCompressedData) { if (!cachedCompressedData) {
const data = await getToolsData(); const data = await getToolsData();
const compressedTools = data.tools.map(tool => { // Filter out concepts and compress remaining tools
const { projectUrl, statusUrl, ...compressedTool } = tool; const compressedTools = data.tools
return compressedTool; .filter(tool => tool.type !== 'concept') // Exclude concepts from AI
}); .map(tool => {
const { projectUrl, statusUrl, ...compressedTool } = tool;
return compressedTool;
});
cachedCompressedData = { cachedCompressedData = {
tools: compressedTools, tools: compressedTools,