import React, { useState, useEffect } from 'react'; import { useParams, Link } from 'react-router-dom'; import { ArrowLeft, ExternalLink, Shield, AlertTriangle, Github, User, Bot } from 'lucide-react'; import { Footer } from '../components/Footer'; import { Advisory, AdvisoryFeed } from '../types'; import { ADVISORY_FEED_URL, LEGACY_ADVISORY_FEED_URL, LOCAL_FEED_PATH, } from '../constants'; export const AdvisoryDetail: React.FC = () => { const { advisoryId } = useParams<{ advisoryId: string }>(); const [advisory, setAdvisory] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { const fetchAdvisory = async () => { if (!advisoryId) return; try { // Try local feed first (dev), then canonical hosted endpoint, then legacy mirror. let response = await fetch(LOCAL_FEED_PATH); if (!response.ok) { response = await fetch(ADVISORY_FEED_URL); } if (!response.ok) { response = await fetch(LEGACY_ADVISORY_FEED_URL); } if (!response.ok) { throw new Error(`Failed to fetch feed: ${response.status}`); } const feed: AdvisoryFeed = await response.json(); const found = feed.advisories.find((a) => a.id === decodeURIComponent(advisoryId)); if (!found) { throw new Error('Advisory not found'); } setAdvisory(found); } catch (err) { console.error('Failed to fetch advisory:', err); setError(err instanceof Error ? err.message : 'Failed to load advisory'); } finally { setLoading(false); } }; fetchAdvisory(); }, [advisoryId]); const formatDate = (dateStr: string) => { try { return new Date(dateStr).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit' }); } catch { return dateStr; } }; const getSeverityClasses = (severity: string) => { switch (severity) { case 'critical': return 'bg-red-500/20 text-red-400 border-red-500/30'; case 'high': return 'bg-orange-500/20 text-orange-400 border-orange-500/30'; case 'medium': return 'bg-yellow-500/20 text-yellow-400 border-yellow-500/30'; default: return 'bg-blue-500/20 text-blue-400 border-blue-500/30'; } }; const getTypeLabel = (type: string) => { switch (type) { case 'malicious_skill': return 'Malicious Skill'; case 'vulnerable_skill': return 'Vulnerable Skill'; case 'prompt_injection': return 'Prompt Injection'; case 'attack_pattern': return 'Attack Pattern'; case 'best_practice': return 'Best Practice'; case 'tampering_attempt': return 'Tampering Attempt'; default: return type; } }; // Determine source - defaults to "Prompt Security Staff" when absent const getSource = (adv: Advisory) => { return adv.source || 'Prompt Security Staff'; }; // Determine if this is a community report const isCommunityReport = advisory?.github_issue_url; if (loading) { return (

Loading advisory...

); } if (error || !advisory) { return (

Advisory Not Found

{error || 'This advisory does not exist'}

Back to Security Feed
); } return (
{/* Back Link */} Back to Security Feed {/* Header */}
{advisory.severity} {advisory.cvss_score && CVSS {advisory.cvss_score}} {getTypeLabel(advisory.type)} Published {formatDate(advisory.published)}

{advisory.id}

{advisory.title}

{/* Description */}

Description

{advisory.description}

{/* Recommended Action */}

Recommended Action

{advisory.action}

{/* Affected Components */} {advisory.affected && advisory.affected.length > 0 && (

Affected Components

    {advisory.affected.map((item, index) => (
  • {item}
  • ))}
)} {/* References */} {advisory.references && advisory.references.length > 0 && (

References

    {advisory.references.map((ref, index) => (
  • {ref}
  • ))}
)} {/* External Link - NVD or GitHub Issue */}
{isCommunityReport && advisory.github_issue_url ? ( View GitHub Report ) : advisory.nvd_url ? ( View on NVD ) : null}
{/* Metadata */}

Metadata

Source
{getSource(advisory)}
{advisory.cvss_score && (
CVSS Score
{advisory.cvss_score}
)}
Type
{getTypeLabel(advisory.type)}
Published
{formatDate(advisory.published)}
{/* Reporter info - subtle display for community reports */} {advisory.reporter && ( <> {advisory.reporter.agent_name && (
Reported By
{advisory.reporter.opener_type === 'agent' ? ( ) : ( )} {advisory.reporter.agent_name}
)} {advisory.reporter.opener_type && (
Reporter Type
{advisory.reporter.opener_type}
)} )}
); };