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;
// Check if this is a method vs software
// Check types
const isMethod = tool.type === 'method';
const isConcept = tool.type === 'concept';
// Check if tool has a valid project URL (means we're hosting it)
const hasValidProjectUrl = tool.projectUrl !== undefined &&
@ -34,7 +36,8 @@ const hasValidProjectUrl = tool.projectUrl !== undefined &&
const hasKnowledgebase = tool.knowledgebase === true;
// 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' :
(tool.license !== 'Proprietary' ? 'card card-oss tool-card' : 'card tool-card');
---
@ -101,7 +104,12 @@ const cardClass = isMethod ? 'card card-method tool-card' :
<!-- Buttons - Fixed at Bottom -->
<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 -->
<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

View File

@ -342,35 +342,6 @@ const sortedTags = Object.entries(tagFrequency)
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
updateMatrixHighlighting();
// Emit custom event with filtered results
@ -418,16 +389,6 @@ const sortedTags = Object.entries(tagFrequency)
if (view === 'hosted') {
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 }));
} else {
filterTools();

View File

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

View File

@ -1904,6 +1904,95 @@ tools:
- jamf
- enterprise
- 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:
- id: incident-response
name: Incident Response & Breach-Untersuchung

View File

@ -112,6 +112,29 @@ const tools = data.tools;
// Initial tools HTML
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
async function checkAuthentication() {
try {
@ -177,7 +200,7 @@ const tools = data.tools;
// Hide filter controls in AI mode - AGGRESSIVE APPROACH
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;
// Hide all checkbox wrappers
const checkboxWrappers = document.querySelectorAll('.checkbox-wrapper');
@ -227,7 +250,7 @@ const tools = data.tools;
// Show filter controls in matrix mode
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 checkboxWrappersMatrix = document.querySelectorAll('.checkbox-wrapper');
const tagHeaderMatrix = document.querySelector('.tag-header') as HTMLElement;
@ -262,7 +285,7 @@ const tools = data.tools;
// Show filter controls in grid mode
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 checkboxWrappersGrid = document.querySelectorAll('.checkbox-wrapper');
const tagHeaderGrid = document.querySelector('.tag-header') as HTMLElement;
@ -318,8 +341,12 @@ const tools = data.tools;
} else {
noResults.style.display = 'none';
// Render filtered tools
filtered.forEach((tool: any) => {
// Apply sorting here - single place for all sorting logic
const currentSortOption = 'default'; // Will be dynamic later
const sortedTools = sortTools(filtered, currentSortOption);
// Render sorted tools
sortedTools.forEach((tool: any) => {
const toolCard = createToolCard(tool);
toolsContainer.appendChild(toolCard);
});
@ -338,6 +365,7 @@ const tools = data.tools;
function createToolCard(tool) {
const isMethod = tool.type === 'method';
const isConcept = tool.type === 'concept';
const hasValidProjectUrl = tool.projectUrl !== undefined &&
tool.projectUrl !== null &&
tool.projectUrl !== "" &&
@ -346,7 +374,8 @@ function createToolCard(tool) {
const hasKnowledgebase = tool.knowledgebase === true;
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' :
(tool.license !== 'Proprietary' ? 'card card-oss tool-card' : 'card tool-card');
cardDiv.className = cardClass;
@ -358,7 +387,7 @@ function createToolCard(tool) {
<div class="tool-card-header">
<h3>${tool.icon ? `<span style="margin-right: 0.5rem; font-size: 1.125rem;">${tool.icon}</span>` : ''}${tool.name}</h3>
<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>' : ''}
</div>
</div>
@ -395,7 +424,7 @@ function createToolCard(tool) {
<polyline points="14 2 14 8 20 8"></polyline>
</svg>
<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>
</div>
</div>
@ -405,7 +434,11 @@ function createToolCard(tool) {
</div>
<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);">
Zur Methode
</a>

View File

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

View File

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