diff --git a/.eslintrc.json b/.eslintrc.json index bffb357..908c39c 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,3 +1,32 @@ { - "extends": "next/core-web-vitals" -} + "extends": "next/core-web-vitals", + "rules": { + "no-restricted-imports": [ + "error", + { + "patterns": [ + { + "group": [ + "**/uptime.config" + ], + "importNames": [ + "workerConfig" + ], + "message": "Do not import workerConfig in client-bundled files. See https://github.com/lyc8503/UptimeFlare/issues/198 for details." + } + ] + } + ] + }, + "overrides": [ + { + "files": [ + "pages/api/data.ts", + "middleware.ts" + ], + "rules": { + "no-restricted-imports": "off" + } + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 1520bba..4d6ba68 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ A more advanced, serverless, and free uptime monitoring & status page solution, powered by Cloudflare Workers, complete with a user-friendly interface. +📢 **[SECURITY 2026/03/04]** A vulnerability that could expose monitor configuration and credentials in `uptime.config.ts` to clients was fixed. Versions between 2025-09-21 (from commit `41257c6`) and 2026-03-04 are affected. **Affected users are strongly advised to upgrade to the latest version.** + 🎉 **[UPDATE 2026/01/03]** I have just migrated UptimeFlare from KV to D1 Database. I also updated the Terraform Cloudflare provider to v5 and improved the deployment process. The data structure has been optimized to resolve long-standing performance issues. New users can deploy directly, while existing users can have a simple auto migration process (upgrade docs below)! Feel free to open an issue if you run into any trouble deploying. diff --git a/pages/incidents.tsx b/pages/incidents.tsx index 9cb5394..480e5e4 100644 --- a/pages/incidents.tsx +++ b/pages/incidents.tsx @@ -2,7 +2,7 @@ import Head from 'next/head' import { Inter } from 'next/font/google' import { MaintenanceConfig, MonitorTarget } from '@/types/config' -import { maintenances, pageConfig, workerConfig } from '@/uptime.config' +import { maintenances, pageConfig } from '@/uptime.config' import Header from '@/components/Header' import { Box, Button, Center, Container, Group, Select } from '@mantine/core' import Footer from '@/components/Footer' @@ -25,7 +25,8 @@ function getSelectedMonth() { function filterIncidentsByMonth( incidents: MaintenanceConfig[], - monthStr: string + monthStr: string, + monitors: MonitorTarget[] ): (Omit & { monitors: MonitorTarget[] })[] { return incidents .filter((incident) => { @@ -35,7 +36,7 @@ function filterIncidentsByMonth( }) .map((e) => ({ ...e, - monitors: (e.monitors || []).map((e) => workerConfig.monitors.find((mon) => mon.id === e)!), + monitors: (e.monitors || []).map((e) => monitors.find((mon) => mon.id === e)!), })) .sort((a, b) => (new Date(a.start) > new Date(b.start) ? -1 : 1)) } @@ -53,7 +54,7 @@ function getPrevNextMonth(monthStr: string) { } } -export default function IncidentsPage() { +export default function IncidentsPage({ monitors }: { monitors: MonitorTarget[] }) { const { t } = useTranslation('common') const [selectedMonitor, setSelectedMonitor] = useState('') const [selectedMonth, setSelectedMonth] = useState(getSelectedMonth()) @@ -64,7 +65,7 @@ export default function IncidentsPage() { return () => window.removeEventListener('hashchange', onHashChange) }, []) - const filteredIncidents = filterIncidentsByMonth(maintenances, selectedMonth) + const filteredIncidents = filterIncidentsByMonth(maintenances, selectedMonth, monitors) const monitorFilteredIncidents = selectedMonitor ? filteredIncidents.filter((i) => i.monitors.find((e) => e.id === selectedMonitor)) : filteredIncidents @@ -73,7 +74,7 @@ export default function IncidentsPage() { const monitorOptions = [ { value: '', label: t('All') }, - ...workerConfig.monitors.map((monitor) => ({ + ...monitors.map((monitor) => ({ value: monitor.id, label: monitor.name, })), @@ -131,3 +132,13 @@ export default function IncidentsPage() { ) } + +export async function getServerSideProps() { + const { workerConfig } = await import('@/uptime.config') + // Only present these values to client + const monitors: MonitorTarget[] = workerConfig.monitors.map((monitor) => ({ + id: monitor.id, + name: monitor.name, + })) as MonitorTarget[] + return { props: { monitors } } +} diff --git a/pages/index.tsx b/pages/index.tsx index b4bb800..8af1e64 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -2,7 +2,7 @@ import Head from 'next/head' import { Inter } from 'next/font/google' import { MonitorTarget } from '@/types/config' -import { maintenances, pageConfig, workerConfig } from '@/uptime.config' +import { maintenances, pageConfig } from '@/uptime.config' import OverallStatus from '@/components/OverallStatus' import Header from '@/components/Header' import MonitorList from '@/components/MonitorList' @@ -69,6 +69,7 @@ export default function Home({ } export async function getServerSideProps() { + const { workerConfig } = await import('@/uptime.config') // Read state as string from storage, to avoid hitting server-side cpu time limit const compactedStateStr = await getFromStore(process.env as any, 'state')