Files
gt-ai-os-community/apps/control-panel-frontend/src/components/ui/error-boundary.tsx
HackWeasel b9dfb86260 GT AI OS Community Edition v2.0.33
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>
2025-12-12 17:04:45 -05:00

105 lines
3.1 KiB
TypeScript

'use client';
import React from 'react';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { AlertTriangle, RefreshCw } from 'lucide-react';
interface ErrorBoundaryState {
hasError: boolean;
error?: Error;
}
interface ErrorBoundaryProps {
children: React.ReactNode;
fallback?: React.ComponentType<{ error?: Error; resetError: () => void }>;
}
class ErrorBoundaryClass extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
constructor(props: ErrorBoundaryProps) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
return { hasError: true, error };
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
console.error('Error caught by boundary:', error, errorInfo);
}
resetError = () => {
this.setState({ hasError: false, error: undefined });
};
render() {
if (this.state.hasError) {
if (this.props.fallback) {
const FallbackComponent = this.props.fallback;
return <FallbackComponent error={this.state.error} resetError={this.resetError} />;
}
return <DefaultErrorFallback error={this.state.error} resetError={this.resetError} />;
}
return this.props.children;
}
}
function DefaultErrorFallback({ error, resetError }: { error?: Error; resetError: () => void }) {
return (
<div className="flex items-center justify-center min-h-[400px] p-6">
<Card className="w-full max-w-md">
<CardHeader className="text-center">
<div className="mx-auto mb-4 w-16 h-16 flex items-center justify-center rounded-full bg-red-100">
<AlertTriangle className="w-8 h-8 text-red-600" />
</div>
<CardTitle className="text-red-600">Something went wrong</CardTitle>
<CardDescription>
An error occurred while loading this page. Please try refreshing.
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
{error && (
<div className="text-sm text-muted-foreground bg-muted p-3 rounded-md">
<strong>Error:</strong> {error.message}
</div>
)}
<div className="flex space-x-2">
<Button onClick={resetError} className="flex-1">
<RefreshCw className="w-4 h-4 mr-2" />
Try Again
</Button>
<Button variant="secondary" onClick={() => window.location.reload()} className="flex-1">
Reload Page
</Button>
</div>
</CardContent>
</Card>
</div>
);
}
// Hook version for functional components
export function useErrorBoundary() {
const [error, setError] = React.useState<Error | null>(null);
const resetError = React.useCallback(() => {
setError(null);
}, []);
const catchError = React.useCallback((error: Error) => {
setError(error);
}, []);
React.useEffect(() => {
if (error) {
throw error;
}
}, [error]);
return { catchError, resetError };
}
export { ErrorBoundaryClass as ErrorBoundary };