From 0603b1b229999ac9252d757af2c10e4db9e0c0b2 Mon Sep 17 00:00:00 2001 From: Pranav Ramesh Date: Sun, 8 Jun 2025 11:05:52 -0400 Subject: [PATCH 1/5] feat: base ui for bounties complete --- src/components/BountyModal.jsx | 133 ++++++++++++++ src/components/StandardNav.jsx | 13 ++ src/pages/bounties.jsx | 310 +++++++++++++++++++++++++++++++++ 3 files changed, 456 insertions(+) create mode 100644 src/components/BountyModal.jsx create mode 100644 src/pages/bounties.jsx diff --git a/src/components/BountyModal.jsx b/src/components/BountyModal.jsx new file mode 100644 index 00000000..c0b90595 --- /dev/null +++ b/src/components/BountyModal.jsx @@ -0,0 +1,133 @@ +import { XMarkIcon, LockClosedIcon } from '@heroicons/react/24/outline'; +import { Fragment } from 'react'; + +const rankColors = { + Gold: { + bg: 'bg-yellow-900/50', + border: 'border-yellow-700/50', + iconText: 'text-yellow-300', + titleText: 'text-yellow-200', + bodyText: 'text-yellow-300/80', + }, + Diamond: { + bg: 'bg-cyan-900/50', + border: 'border-cyan-700/50', + iconText: 'text-cyan-300', + titleText: 'text-cyan-200', + bodyText: 'text-cyan-300/80', + }, +}; + +export function BountyModal({ bounty, onClose }) { + if (!bounty) return null; + + const rewardTiers = [ + { name: 'Critical', value: bounty.rewards.critical }, + { name: 'High', value: bounty.rewards.high }, + { name: 'Medium', value: bounty.rewards.medium }, + { name: 'Low', value: bounty.rewards.low }, + ]; + + const colors = bounty.requiredRank ? rankColors[bounty.requiredRank] || rankColors.Gold : {}; + + return ( +
+
e.stopPropagation()}> + +
+
+ {`${bounty.company} +
+

{bounty.title}

+

{bounty.company}

+
+
+ {bounty.requiredRank && ( +
+ +
+

Rank Required

+

+ This bounty is exclusive to researchers with the {bounty.requiredRank} rank. +

+
+
+ )} +

{bounty.description}

+ +
+
+

Rewards

+
+ {rewardTiers.map(tier => ( +
+ {tier.name} + ${tier.value} +
+ ))} +
+
+
+

Scope & Vulnerabilities

+
+

Targets

+
    + {bounty.targets.map(target =>
  • {target}
  • )} +
+
+
+

Vulnerability Types

+
+ {bounty.vuln_types.map(vuln => ( + + {vuln} + + ))} +
+
+
+
+ +
+

Submission History

+ {bounty.history && bounty.history.length > 0 ? ( +
+
+
+ + + + + + + + + + + {bounty.history.map((finding, index) => ( + + + + + + + ))} + +
UserVulnerabilityRewardDate
{finding.user}{finding.vulnerability}${finding.reward.toLocaleString()}{finding.date}
+
+
+
+ ) : ( +

No public submissions yet.

+ )} +
+
+
+
+ ); +} \ No newline at end of file diff --git a/src/components/StandardNav.jsx b/src/components/StandardNav.jsx index 0df2b72c..8ac27c66 100644 --- a/src/components/StandardNav.jsx +++ b/src/components/StandardNav.jsx @@ -12,6 +12,7 @@ import { ArrowRightIcon, EllipsisVerticalIcon, ShieldCheckIcon, + GiftIcon, BellIcon, UserGroupIcon, } from '@heroicons/react/24/outline'; @@ -399,6 +400,18 @@ export function StandardNav({ guestAllowed, alignCenter = true }) { + +
+ +
+
+

Bounties

+

Earn rewards for solving challenges

+
+ +

Launch your Bug Bounty

+

+ Secure your startup with our global community of security researchers. We offer managed bounties, seamless deployment, and actionable reports. +

+ + + ); +} + +function TopBounties({ bounties, onBountyClick }) { + const sortedBounties = [...bounties].sort((a, b) => { + const maxRewardA = Math.max(...Object.values(a.rewards).map(r => parseInt(r.replace(',', '')))); + const maxRewardB = Math.max(...Object.values(b.rewards).map(r => parseInt(r.replace(',', '')))); + return maxRewardB - maxRewardA; + }).slice(0, 3); + + return ( +
+

Top Bounties

+ +
+ ); +} + +function RecentPayouts({ bounties, onBountyClick }) { + const allPayouts = bounties.flatMap(bounty => + bounty.history.map(payout => ({ ...payout, bounty })) + ).sort((a, b) => new Date(b.date) - new Date(a.date)).slice(0, 5); + + return ( +
+

Recent Payouts

+ +
+ ); +} + +export default function Bounties() { + const [bounties, setBounties] = useState([]); + const [loading, setLoading] = useState(true); + const [selectedBounty, setSelectedBounty] = useState(null); + + useEffect(() => { + setTimeout(() => { + setBounties(mockBounties); + setLoading(false); + }, 500); + }, []); + + const getRewardRange = (rewards) => { + const rewardValues = Object.values(rewards).map(r => parseInt(r.replace(',', ''))); + const min = Math.min(...rewardValues); + const max = Math.max(...rewardValues); + return `$${min.toLocaleString()} - $${max.toLocaleString()}`; + }; + + return ( + <> + + Bug Bounties | CTFGuide + + + + + +
+
+
+

+ Bounties +

+

+ Help secure innovative startups and get rewarded for finding vulnerabilities. +

+
+ +
+
+ {loading ? ( +
+
+
+ ) : ( +
+ {bounties.map((bounty) => { + const hasRank = !!bounty.requiredRank; + const colors = hasRank ? rankColors[bounty.requiredRank] || rankColors.Gold : {}; + const isLocked = hasRank; // assume user is not high enough rank + + return ( +
+ + {isLocked && ( +
+
+ +
+

Locked

+

Requires {bounty.requiredRank} Rank

+
+
+ + +
+ )} +
+ ); + })} +
+ )} +
+ +
+
+ setSelectedBounty(null)} /> +
+ +