Add warn for service role keys

This commit is contained in:
Terry Sutton committed 2024-12-18 23:18:45 -03:30
1 parent 1a4e0243be
commit e7facf9b8d
6 files changed
+143 -3

No files matched your search

@@ -22,7 +22,15 @@ import { withAuth } from 'hooks/misc/withAuth'
import { IS_PLATFORM, PROJECT_STATUS } from 'lib/constants'
import { useAppStateSnapshot } from 'state/app-state'
import { useDatabaseSelectorStateSnapshot } from 'state/database-selector'
import { cn, ResizableHandle, ResizablePanel, ResizablePanelGroup, Sheet, SheetContent } from 'ui'
import {
Alert_Shadcn_,
cn,
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
Sheet,
SheetContent,
} from 'ui'
import AppLayout from '../AppLayout/AppLayout'
import EnableBranchingModal from '../AppLayout/EnableBranchingButton/EnableBranchingModal'
import BuildingState from './BuildingState'
@@ -43,6 +51,7 @@ import RestoreFailedState from './RestoreFailedState'
import RestoringState from './RestoringState'
import { UpgradingState } from './UpgradingState'
import MobileSheetNav from 'ui-patterns/MobileSheetNav/MobileSheetNav'
import { useServiceRoleKeyLeakQuery } from 'data/lint/service-role-key-leak-query'
// [Joshen] This is temporary while we unblock users from managing their project
// if their project is not responding well for any reason. Eventually needs a bit of an overhaul
@@ -121,6 +130,9 @@ const ProjectLayout = forwardRef<HTMLDivElement, PropsWithChildren<ProjectLayout
const [isClient, setIsClient] = useState(false)
const { data: serviceRoleKeyLeakData } = useServiceRoleKeyLeakQuery({ projectRef: projectRef })
console.log('serviceRoleKeyLeakData', serviceRoleKeyLeakData)
useEffect(() => {
setIsClient(true)
}, [])
@@ -224,6 +236,9 @@ const ProjectLayout = forwardRef<HTMLDivElement, PropsWithChildren<ProjectLayout
</div>
) : (
<ContentWrapper isLoading={isLoading} isBlocking={isBlocking}>
<Alert_Shadcn_ variant="destructive">
Your `service_role` key has been leaked...warning / docs / etc
</Alert_Shadcn_>
<ResourceExhaustionWarningBanner />
{children}
</ContentWrapper>
+2
View File
@@ -1,3 +1,5 @@
export const lintKeys = {
lint: (projectRef: string | undefined) => ['projects', projectRef, 'lint'] as const,
serviceRoleKeyLeak: (projectRef: string | undefined) =>
['projects', projectRef, 'service-role-key-leak'] as const,
}
@@ -0,0 +1,57 @@
import { UseQueryOptions, useQuery } from '@tanstack/react-query'
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
import { BASE_PATH, PROJECT_STATUS } from 'lib/constants'
import { ResponseError } from 'types'
import { lintKeys } from './keys'
export type ServiceRoleKeyLeakVariables = {
projectRef?: string
}
// TODO: type this
export type ServiceRoleKeyLeakResponse = any[]
export async function checkServiceRoleKeyLeak(
{ projectRef }: ServiceRoleKeyLeakVariables,
signal?: AbortSignal
) {
if (!projectRef) throw new Error('Project ref is required')
const res = await fetch(`${BASE_PATH}/api/check-service-role-key-leak`, {
headers: {
'x-project': projectRef,
},
signal,
})
if (!res.ok) {
const error = await res.json()
throw new Error(error.error?.message || 'Failed to fetch service role key leak data')
}
return res.json()
}
export type ServiceRoleKeyLeakData = Awaited<ReturnType<typeof checkServiceRoleKeyLeak>>
export type ServiceRoleKeyLeakError = ResponseError
export const useServiceRoleKeyLeakQuery = <TData = ServiceRoleKeyLeakData>(
{ projectRef }: ServiceRoleKeyLeakVariables,
{
enabled = true,
...options
}: UseQueryOptions<ServiceRoleKeyLeakData, ServiceRoleKeyLeakError, TData> = {}
) => {
const project = useSelectedProject()
const isActive = project?.status === PROJECT_STATUS.ACTIVE_HEALTHY
return useQuery<ServiceRoleKeyLeakData, ServiceRoleKeyLeakError, TData>(
lintKeys.serviceRoleKeyLeak(projectRef),
({ signal }) => checkServiceRoleKeyLeak({ projectRef }, signal),
{
enabled: enabled && typeof projectRef !== 'undefined' && isActive,
...options,
}
)
}
@@ -9,8 +9,18 @@ import { usageKeys } from './keys'
export async function getResourceWarnings(signal?: AbortSignal) {
const { data, error } = await get(`/platform/projects-resource-warnings`, { signal })
if (error) handleError(error)
return data
// for testing purposes, return a hardcoded array of resource warnings
return [
{
project: 'aclodpjssdgdxbvmybdv', //replace with your ref to get an example warning
is_readonly_mode_enabled: false,
disk_io_exhaustion: null,
disk_space_exhaustion: null,
cpu_exhaustion: 'critical',
memory_and_swap_exhaustion: null,
},
] as ResourceWarning[]
//return data
}
export type ResourceWarning = components['schemas']['ProjectResourceWarningsResponse']
+2
View File
@@ -16,6 +16,8 @@ const HOSTED_SUPPORTED_API_URLS = [
'/ai/sql/cron',
'/ai/docs',
'/get-ip-address',
'/get-ip-address',
'/check-service-role-key-leak',
]
export function middleware(request: NextRequest) {
@@ -0,0 +1,54 @@
import apiWrapper from 'lib/api/apiWrapper'
import { NextApiRequest, NextApiResponse } from 'next'
async function handler(req: NextApiRequest, res: NextApiResponse) {
const { method } = req
switch (method) {
case 'GET':
return handleGet(req, res)
default:
res.setHeader('Allow', ['GET'])
res.status(405).json({ data: null, error: { message: `Method ${method} Not Allowed` } })
}
}
async function handleGet(req: NextApiRequest, res: NextApiResponse) {
const projectRef = req.headers['x-project'] as string
const project = 'bzembqluzmwrjuidwley'
console.log('the ref', projectRef)
if (!project) {
return res.status(400).json({ error: 'Project reference is required' })
}
try {
const response = await fetch(
`https://api.logflare.app/api/endpoints/query/exposed%20service_role%20advisor%20hack?project=${project}`,
{
method: 'GET',
headers: {
'Content-Type': 'application/json; charset=utf-8',
'X-API-KEY': process.env.LOGFLARE_API_KEY!,
},
}
)
if (!response.ok) {
throw new Error(`Logflare API responded with status ${response.status}`)
}
const data = await response.json()
return res.json(data)
} catch (error) {
console.error('Error fetching service role key leak data:', error)
return res.status(500).json({
error:
'There was an unknown error fetching the service role key leak data. Please try again.',
})
}
}
const wrapper = (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler)
export default wrapper