Security hardening release addressing CodeQL and Dependabot alerts: - Fix stack trace exposure in error responses - Add SSRF protection with DNS resolution checking - Implement proper URL hostname validation (replaces substring matching) - Add centralized path sanitization to prevent path traversal - Fix ReDoS vulnerability in email validation regex - Improve HTML sanitization in validation utilities - Fix capability wildcard matching in auth utilities - Update glob dependency to address CVE - Add CodeQL suppression comments for verified false positives 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
340 lines
11 KiB
Python
340 lines
11 KiB
Python
"""
|
|
Category Model for GT 2.0 Agent Discovery
|
|
|
|
Implements a simple hierarchical category system for organizing agents.
|
|
Follows GT 2.0's principle of "Clarity Over Complexity"
|
|
- Simple parent-child relationships
|
|
- System categories that cannot be deleted
|
|
- Tenant-specific and global categories
|
|
"""
|
|
|
|
from datetime import datetime
|
|
from typing import Optional, Dict, Any, List
|
|
import uuid
|
|
|
|
from sqlalchemy import Column, Integer, String, Text, DateTime, Boolean, ForeignKey
|
|
from sqlalchemy.sql import func
|
|
from sqlalchemy.orm import relationship
|
|
|
|
from app.core.database import Base
|
|
|
|
|
|
class Category(Base):
|
|
"""Category model for organizing agents and resources
|
|
|
|
GT 2.0 Design: Simple hierarchical categories without complex taxonomies
|
|
"""
|
|
|
|
__tablename__ = "categories"
|
|
|
|
# Primary Key
|
|
id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()), index=True)
|
|
slug = Column(String(100), unique=True, nullable=False, index=True) # URL-safe identifier
|
|
|
|
# Category Details
|
|
name = Column(String(100), nullable=False, index=True)
|
|
display_name = Column(String(100), nullable=False)
|
|
description = Column(Text, nullable=True)
|
|
icon = Column(String(10), nullable=True) # Emoji or icon code
|
|
color = Column(String(20), nullable=True) # Hex color code for UI
|
|
|
|
# Hierarchy (simple parent-child)
|
|
parent_id = Column(String(36), ForeignKey("categories.id"), nullable=True, index=True)
|
|
|
|
# Scope
|
|
is_system = Column(Boolean, nullable=False, default=False) # Protected from deletion
|
|
is_global = Column(Boolean, nullable=False, default=True) # Available to all tenants
|
|
|
|
# Display Order
|
|
sort_order = Column(Integer, nullable=False, default=0)
|
|
|
|
# Usage Statistics (cached)
|
|
assistant_count = Column(Integer, nullable=False, default=0)
|
|
dataset_count = Column(Integer, nullable=False, default=0)
|
|
|
|
# Timestamps
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
|
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False)
|
|
|
|
# Relationships
|
|
parent = relationship("Category", remote_side=[id], backref="children")
|
|
|
|
def __repr__(self) -> str:
|
|
return f"<Category(id={self.id}, name='{self.name}', slug='{self.slug}')>"
|
|
|
|
def to_dict(self, include_children: bool = False) -> Dict[str, Any]:
|
|
"""Convert to dictionary for API responses"""
|
|
data = {
|
|
"id": self.id,
|
|
"slug": self.slug,
|
|
"name": self.name,
|
|
"display_name": self.display_name,
|
|
"description": self.description,
|
|
"icon": self.icon,
|
|
"color": self.color,
|
|
"parent_id": self.parent_id,
|
|
"is_system": self.is_system,
|
|
"is_global": self.is_global,
|
|
"sort_order": self.sort_order,
|
|
"assistant_count": self.assistant_count,
|
|
"dataset_count": self.dataset_count,
|
|
"created_at": self.created_at.isoformat() if self.created_at else None,
|
|
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
|
|
}
|
|
|
|
if include_children and self.children:
|
|
data["children"] = [child.to_dict() for child in self.children]
|
|
|
|
return data
|
|
|
|
def get_full_path(self) -> str:
|
|
"""Get full category path (e.g., 'AI Tools > Research > Academic')"""
|
|
if not self.parent_id:
|
|
return self.display_name
|
|
|
|
# Simple recursion to build path
|
|
parent_path = self.parent.get_full_path() if self.parent else ""
|
|
return f"{parent_path} > {self.display_name}" if parent_path else self.display_name
|
|
|
|
def is_descendant_of(self, ancestor_id: int) -> bool:
|
|
"""Check if this category is a descendant of another"""
|
|
if not self.parent_id:
|
|
return False
|
|
|
|
if self.parent_id == ancestor_id:
|
|
return True
|
|
|
|
return self.parent.is_descendant_of(ancestor_id) if self.parent else False
|
|
|
|
def get_all_descendants(self) -> List["Category"]:
|
|
"""Get all descendant categories"""
|
|
descendants = []
|
|
|
|
if self.children:
|
|
for child in self.children:
|
|
descendants.append(child)
|
|
descendants.extend(child.get_all_descendants())
|
|
|
|
return descendants
|
|
|
|
def update_counts(self, assistant_delta: int = 0, dataset_delta: int = 0) -> None:
|
|
"""Update resource counts for this category"""
|
|
self.assistant_count = max(0, self.assistant_count + assistant_delta)
|
|
self.dataset_count = max(0, self.dataset_count + dataset_delta)
|
|
self.updated_at = datetime.utcnow()
|
|
|
|
|
|
# GT 2.0 Default System Categories
|
|
DEFAULT_CATEGORIES = [
|
|
# Top-level categories
|
|
{
|
|
"slug": "research",
|
|
"name": "Research & Analysis",
|
|
"display_name": "Research & Analysis",
|
|
"description": "Agents for research, analysis, and information synthesis",
|
|
"icon": "🔍",
|
|
"color": "#3B82F6", # Blue
|
|
"is_system": True,
|
|
"is_global": True,
|
|
"sort_order": 10,
|
|
},
|
|
{
|
|
"slug": "development",
|
|
"name": "Software Development",
|
|
"display_name": "Software Development",
|
|
"description": "Coding, debugging, and development tools",
|
|
"icon": "💻",
|
|
"color": "#10B981", # Green
|
|
"is_system": True,
|
|
"is_global": True,
|
|
"sort_order": 20,
|
|
},
|
|
{
|
|
"slug": "cybersecurity",
|
|
"name": "Cybersecurity",
|
|
"display_name": "Cybersecurity",
|
|
"description": "Security analysis, threat detection, and incident response",
|
|
"icon": "🛡️",
|
|
"color": "#EF4444", # Red
|
|
"is_system": True,
|
|
"is_global": True,
|
|
"sort_order": 30,
|
|
},
|
|
{
|
|
"slug": "education",
|
|
"name": "Education & Training",
|
|
"display_name": "Education & Training",
|
|
"description": "Educational agents and AI literacy tools",
|
|
"icon": "🎓",
|
|
"color": "#8B5CF6", # Purple
|
|
"is_system": True,
|
|
"is_global": True,
|
|
"sort_order": 40,
|
|
},
|
|
{
|
|
"slug": "creative",
|
|
"name": "Creative & Content",
|
|
"display_name": "Creative & Content",
|
|
"description": "Writing, design, and creative content generation",
|
|
"icon": "✨",
|
|
"color": "#F59E0B", # Amber
|
|
"is_system": True,
|
|
"is_global": True,
|
|
"sort_order": 50,
|
|
},
|
|
{
|
|
"slug": "analytics",
|
|
"name": "Data & Analytics",
|
|
"display_name": "Data & Analytics",
|
|
"description": "Data analysis, visualization, and insights",
|
|
"icon": "📊",
|
|
"color": "#06B6D4", # Cyan
|
|
"is_system": True,
|
|
"is_global": True,
|
|
"sort_order": 60,
|
|
},
|
|
{
|
|
"slug": "business",
|
|
"name": "Business & Operations",
|
|
"display_name": "Business & Operations",
|
|
"description": "Business analysis, planning, and operations",
|
|
"icon": "💼",
|
|
"color": "#64748B", # Slate
|
|
"is_system": True,
|
|
"is_global": True,
|
|
"sort_order": 70,
|
|
},
|
|
{
|
|
"slug": "personal",
|
|
"name": "Personal Productivity",
|
|
"display_name": "Personal Productivity",
|
|
"description": "Personal agents and productivity tools",
|
|
"icon": "🚀",
|
|
"color": "#14B8A6", # Teal
|
|
"is_system": True,
|
|
"is_global": True,
|
|
"sort_order": 80,
|
|
},
|
|
{
|
|
"slug": "custom",
|
|
"name": "Custom & Specialized",
|
|
"display_name": "Custom & Specialized",
|
|
"description": "Custom-built and specialized agents",
|
|
"icon": "⚙️",
|
|
"color": "#71717A", # Zinc
|
|
"is_system": True,
|
|
"is_global": True,
|
|
"sort_order": 90,
|
|
},
|
|
]
|
|
|
|
|
|
# Sub-categories (examples)
|
|
DEFAULT_SUBCATEGORIES = [
|
|
# Research subcategories
|
|
{
|
|
"slug": "research-academic",
|
|
"name": "Academic Research",
|
|
"display_name": "Academic Research",
|
|
"description": "Academic papers, citations, and literature review",
|
|
"icon": "📚",
|
|
"parent_slug": "research", # Will be resolved to parent_id
|
|
"is_system": True,
|
|
"is_global": True,
|
|
"sort_order": 11,
|
|
},
|
|
{
|
|
"slug": "research-market",
|
|
"name": "Market Research",
|
|
"display_name": "Market Research",
|
|
"description": "Market analysis, competitor research, and trends",
|
|
"icon": "📈",
|
|
"parent_slug": "research",
|
|
"is_system": True,
|
|
"is_global": True,
|
|
"sort_order": 12,
|
|
},
|
|
|
|
# Development subcategories
|
|
{
|
|
"slug": "dev-web",
|
|
"name": "Web Development",
|
|
"display_name": "Web Development",
|
|
"description": "Frontend, backend, and full-stack development",
|
|
"icon": "🌐",
|
|
"parent_slug": "development",
|
|
"is_system": True,
|
|
"is_global": True,
|
|
"sort_order": 21,
|
|
},
|
|
{
|
|
"slug": "dev-mobile",
|
|
"name": "Mobile Development",
|
|
"display_name": "Mobile Development",
|
|
"description": "iOS, Android, and cross-platform development",
|
|
"icon": "📱",
|
|
"parent_slug": "development",
|
|
"is_system": True,
|
|
"is_global": True,
|
|
"sort_order": 22,
|
|
},
|
|
{
|
|
"slug": "dev-devops",
|
|
"name": "DevOps & Infrastructure",
|
|
"display_name": "DevOps & Infrastructure",
|
|
"description": "CI/CD, containerization, and infrastructure",
|
|
"icon": "🔧",
|
|
"parent_slug": "development",
|
|
"is_system": True,
|
|
"is_global": True,
|
|
"sort_order": 23,
|
|
},
|
|
|
|
# Cybersecurity subcategories
|
|
{
|
|
"slug": "cyber-analysis",
|
|
"name": "Threat Analysis",
|
|
"display_name": "Threat Analysis",
|
|
"description": "Threat detection, analysis, and intelligence",
|
|
"icon": "🔍",
|
|
"parent_slug": "cybersecurity",
|
|
"is_system": True,
|
|
"is_global": True,
|
|
"sort_order": 31,
|
|
},
|
|
{
|
|
"slug": "cyber-incident",
|
|
"name": "Incident Response",
|
|
"display_name": "Incident Response",
|
|
"description": "Incident handling and forensics",
|
|
"icon": "🚨",
|
|
"parent_slug": "cybersecurity",
|
|
"is_system": True,
|
|
"is_global": True,
|
|
"sort_order": 32,
|
|
},
|
|
|
|
# Education subcategories
|
|
{
|
|
"slug": "edu-ai-literacy",
|
|
"name": "AI Literacy",
|
|
"display_name": "AI Literacy",
|
|
"description": "Understanding and working with AI systems",
|
|
"icon": "🤖",
|
|
"parent_slug": "education",
|
|
"is_system": True,
|
|
"is_global": True,
|
|
"sort_order": 41,
|
|
},
|
|
{
|
|
"slug": "edu-critical-thinking",
|
|
"name": "Critical Thinking",
|
|
"display_name": "Critical Thinking",
|
|
"description": "Logic, reasoning, and problem-solving",
|
|
"icon": "🧠",
|
|
"parent_slug": "education",
|
|
"is_system": True,
|
|
"is_global": True,
|
|
"sort_order": 42,
|
|
},
|
|
] |