import React, { useState, useEffect, useMemo } from 'react'; import { Rss, RefreshCw, Loader2, AlertTriangle, ChevronLeft, ChevronRight, Download, Users, AlertCircle } from 'lucide-react'; import { Link } from 'react-router-dom'; import { Footer } from '../components/Footer'; import { AdvisoryCard } from '../components/AdvisoryCard'; import { Advisory, AdvisoryFeed, AdvisoryPlatformFilter } from '../types'; import { isCorePlatformSlug, normalizePlatformSlug } from '../utils/advisoryPlatforms'; import { ADVISORY_FEED_URL, LEGACY_ADVISORY_FEED_URL, LOCAL_FEED_PATH, } from '../constants'; const ITEMS_PER_PAGE = 9; type SeverityFilter = 'all' | Advisory['severity']; type FilterTabOption = { value: T; label: string; active: string; inactive: string }; const SEVERITY_TABS = [ { value: 'all', label: 'All', active: 'bg-clawd-accent text-white', inactive: 'bg-clawd-800 text-gray-400 border border-clawd-700 hover:border-clawd-accent/50' }, { value: 'critical', label: 'Critical', active: 'bg-red-500/20 text-red-400 border-2 border-red-400', inactive: 'bg-clawd-800 text-gray-400 border border-clawd-700 hover:border-red-400/50' }, { value: 'high', label: 'High', active: 'bg-orange-500/20 text-orange-400 border-2 border-orange-400', inactive: 'bg-clawd-800 text-gray-400 border border-clawd-700 hover:border-orange-400/50' }, { value: 'medium', label: 'Medium', active: 'bg-yellow-500/20 text-yellow-400 border-2 border-yellow-400', inactive: 'bg-clawd-800 text-gray-400 border border-clawd-700 hover:border-yellow-400/50' }, { value: 'low', label: 'Low', active: 'bg-blue-500/20 text-blue-400 border-2 border-blue-400', inactive: 'bg-clawd-800 text-gray-400 border border-clawd-700 hover:border-blue-400/50' }, ] as const satisfies ReadonlyArray>; const PLATFORM_TABS = [ { value: 'all', label: 'All Platforms', active: 'bg-clawd-accent text-white', inactive: 'bg-clawd-800 text-gray-400 border border-clawd-700 hover:border-clawd-accent/50' }, { value: 'openclaw', label: 'OpenClaw', active: 'bg-clawd-accent/20 text-clawd-accent border-2 border-clawd-accent', inactive: 'bg-clawd-800 text-gray-400 border border-clawd-700 hover:border-clawd-accent/50' }, { value: 'nanoclaw', label: 'NanoClaw', active: 'bg-clawd-secondary/20 text-clawd-secondary border-2 border-clawd-secondary', inactive: 'bg-clawd-800 text-gray-400 border border-clawd-700 hover:border-clawd-secondary/50' }, { value: 'hermes', label: 'Hermes', active: 'bg-emerald-500/20 text-emerald-300 border-2 border-emerald-400', inactive: 'bg-clawd-800 text-gray-400 border border-clawd-700 hover:border-emerald-400/50' }, { value: 'picoclaw', label: 'Picoclaw', active: 'bg-cyan-500/20 text-cyan-300 border-2 border-cyan-400', inactive: 'bg-clawd-800 text-gray-400 border border-clawd-700 hover:border-cyan-400/50' }, { value: 'other', label: 'Other', active: 'bg-clawd-600/40 text-gray-100 border-2 border-clawd-500', inactive: 'bg-clawd-800 text-gray-400 border border-clawd-700 hover:border-clawd-500/50' }, ] as const satisfies ReadonlyArray>; const FilterTabs = ({ tabs, selected, onSelect, }: { tabs: ReadonlyArray>; selected: T; onSelect: (value: T) => void; }) => (
{tabs.map(({ value, label, active, inactive }) => ( ))}
); export const FeedSetup: React.FC = () => { const [advisories, setAdvisories] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [lastUpdated, setLastUpdated] = useState(null); const [currentPage, setCurrentPage] = useState(1); const [selectedSeverity, setSelectedSeverity] = useState('all'); const [selectedPlatform, setSelectedPlatform] = useState('all'); useEffect(() => { const fetchAdvisories = async () => { setLoading(true); setError(null); 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(); setAdvisories(feed.advisories || []); setLastUpdated(feed.updated); } catch (err) { console.error('Failed to fetch advisories:', err); setError('Unable to load security advisories. The feed may be temporarily unavailable.'); setAdvisories([]); } finally { setLoading(false); } }; fetchAdvisories(); }, []); const filteredAdvisories = useMemo( () => advisories.filter((a) => { if (selectedSeverity !== 'all' && a.severity !== selectedSeverity) { return false; } if (selectedPlatform === 'all') { return true; } const advisoryPlatforms = (a.platforms ?? []) .map(normalizePlatformSlug) .filter(Boolean); if (selectedPlatform === 'other') { return advisoryPlatforms.some((platform) => !isCorePlatformSlug(platform)); } return advisoryPlatforms.length === 0 || advisoryPlatforms.includes(selectedPlatform); }), [advisories, selectedSeverity, selectedPlatform], ); useEffect(() => { setCurrentPage(1); }, [advisories, selectedSeverity, selectedPlatform]); const formatDate = (dateStr: string) => { try { return new Date(dateStr).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' }); } catch { return dateStr; } }; // Pagination calculations const totalPages = Math.ceil(filteredAdvisories.length / ITEMS_PER_PAGE); const startIndex = (currentPage - 1) * ITEMS_PER_PAGE; const endIndex = startIndex + ITEMS_PER_PAGE; const currentAdvisories = filteredAdvisories.slice(startIndex, endIndex); const goToPage = (page: number) => { setCurrentPage(Math.max(1, Math.min(page, totalPages))); window.scrollTo({ top: 0, behavior: 'smooth' }); }; return (

Security Hardening Feed

A continuous stream of security advisories from NVD CVE data and staff-approved community reports. This feed is automatically updated with OpenClaw, NanoClaw, Hermes, and Picoclaw-related vulnerabilities and verified security incidents.

{lastUpdated && (

Last updated: {formatDate(lastUpdated)}

)}
setSelectedSeverity(value as SeverityFilter)} /> setSelectedPlatform(value as AdvisoryPlatformFilter)} /> {loading ? (
Loading advisories...
) : error ? (
{error}
) : filteredAdvisories.length === 0 ? (

{advisories.length === 0 ? 'No security advisories at this time. Check back later.' : 'No advisories found for the selected filters.'}

) : ( <>
{currentAdvisories.map((item) => ( ))}
{/* Pagination Controls */} {totalPages > 1 && (
Page {currentPage} of {totalPages}
)} {filteredAdvisories.length > 0 && (

Showing {startIndex + 1}-{Math.min(endIndex, filteredAdvisories.length)} of {filteredAdvisories.length} advisories {(selectedSeverity !== 'all' || selectedPlatform !== 'all') && ` (${advisories.length} total)`}

)} )}

Agent Integration Instructions

SKILLS-BASED

Activate following skills from the suite to participate.

{/* clawsec-feed skill */} 📡

clawsec-feed

Subscribe your agent to receive security advisories via heartbeat. Automatically polls the feed and notifies you of critical and high severity threats.

{/* clawtributor skill */} 🤝

clawtributor

Opt-in to community incident reporting. Your agent can automatically submit security reports when it detects malicious prompts or suspicious skill behavior.

Collective Security

When agents share threat intelligence, the entire ecosystem becomes safer. Reports are reviewed by staff before publication to ensure quality and privacy.

Human looking to contribute

Found a prompt injection vector or malicious skill? Help the community by submitting a security incident report via GitHub Issue. All submissions are reviewed by staff before publication to the advisory feed.

Submit Report
); };