2
0

fix(results): 🔒️ Improve delete security checks

This commit is contained in:
Baptiste Arnaud
2022-06-18 09:41:23 +02:00
parent 4e6217597c
commit 9352587298
6 changed files with 25 additions and 24 deletions

View File

@ -4,7 +4,6 @@ import { SubmissionsTable } from 'components/results/SubmissionsTable'
import React, { useCallback, useMemo, useState } from 'react' import React, { useCallback, useMemo, useState } from 'react'
import { import {
convertResultsToTableData, convertResultsToTableData,
deleteAllResults,
deleteResults, deleteResults,
getAllResults, getAllResults,
useResults, useResults,
@ -70,10 +69,11 @@ export const SubmissionsContent = ({
const selectedIds = (results ?? []) const selectedIds = (results ?? [])
.filter((_, idx) => selectedIndices.includes(idx)) .filter((_, idx) => selectedIndices.includes(idx))
.map((result) => result.id) .map((result) => result.id)
const { error } = const { error } = await deleteResults(
totalSelected === totalResults workspaceId,
? await deleteAllResults(typebotId) typebotId,
: await deleteResults(typebotId, selectedIds) totalSelected === totalResults ? [] : selectedIds
)
if (error) showToast({ description: error.message, title: error.name }) if (error) showToast({ description: error.message, title: error.name })
else { else {
mutate( mutate(

View File

@ -2,7 +2,7 @@ import { rest, setupWorker } from 'msw'
import { setupServer } from 'msw/node' import { setupServer } from 'msw/node'
const handlers = () => [ const handlers = () => [
rest.get('/api/auth/session', (req, res, ctx) => { rest.get('http://localhost:3000/api/auth/session', (req, res, ctx) => {
const authenticatedUser = JSON.parse( const authenticatedUser = JSON.parse(
typeof localStorage !== 'undefined' typeof localStorage !== 'undefined'
? (localStorage.getItem('authenticatedUser') as string) ? (localStorage.getItem('authenticatedUser') as string)

View File

@ -15,13 +15,13 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const user = await getAuthenticatedUser(req) const user = await getAuthenticatedUser(req)
if (!user) return notAuthenticated(res) if (!user) return notAuthenticated(res)
const workspaceId = req.query.workspaceId as string | undefined const workspaceId = req.query.workspaceId as string | undefined
if (!workspaceId) return badRequest(res, 'workspaceId is required')
const workspace = await prisma.workspace.findFirst({
where: { id: workspaceId, members: { some: { userId: user.id } } },
select: { plan: true },
})
if (!workspace) return forbidden(res)
if (req.method === 'GET') { if (req.method === 'GET') {
if (!workspaceId) return badRequest(res, 'workspaceId is required')
const workspace = await prisma.workspace.findFirst({
where: { id: workspaceId, members: { some: { userId: user.id } } },
select: { plan: true },
})
if (!workspace) return forbidden(res)
const typebotId = req.query.typebotId.toString() const typebotId = req.query.typebotId.toString()
const lastResultId = req.query.lastResultId?.toString() const lastResultId = req.query.lastResultId?.toString()
const take = parseInt(req.query.limit?.toString()) const take = parseInt(req.query.limit?.toString())
@ -46,7 +46,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
return res.status(200).send({ results }) return res.status(200).send({ results })
} }
if (req.method === 'DELETE') { if (req.method === 'DELETE') {
const typebotId = req.query.typebotId.toString() const typebotId = req.query.typebotId as string
const ids = req.query.ids as string[] const ids = req.query.ids as string[]
const results = await prisma.result.deleteMany({ const results = await prisma.result.deleteMany({
where: { where: {

View File

@ -57,10 +57,12 @@ test.describe('Results page', () => {
await deleteButtonInConfirmDialog(page).click() await deleteButtonInConfirmDialog(page).click()
await expect(page.locator('text=content199')).toBeHidden() await expect(page.locator('text=content199')).toBeHidden()
await expect(page.locator('text=content198')).toBeHidden() await expect(page.locator('text=content198')).toBeHidden()
await page.waitForTimeout(1000)
await page.click('[data-testid="checkbox"] >> nth=0') await page.click('[data-testid="checkbox"] >> nth=0')
await page.click('button:has-text("Delete198")') await page.click('button:has-text("Delete198")')
await deleteButtonInConfirmDialog(page).click() await deleteButtonInConfirmDialog(page).click()
await expect(page.locator(':nth-match(tr, 2)')).toBeHidden() await page.waitForTimeout(1000)
expect(await page.locator('tr').count()).toBe(1)
}) })
test('submissions table should have infinite scroll', async ({ page }) => { test('submissions table should have infinite scroll', async ({ page }) => {

View File

@ -76,10 +76,15 @@ export const useResults = ({
} }
} }
export const deleteResults = async (typebotId: string, ids: string[]) => { export const deleteResults = async (
workspaceId: string,
typebotId: string,
ids: string[]
) => {
const params = stringify( const params = stringify(
{ {
ids, ids,
workspaceId,
}, },
{ indices: false } { indices: false }
) )
@ -89,12 +94,6 @@ export const deleteResults = async (typebotId: string, ids: string[]) => {
}) })
} }
export const deleteAllResults = async (typebotId: string) =>
sendRequest({
url: `/api/typebots/${typebotId}/results`,
method: 'DELETE',
})
export const getAllResults = async (workspaceId: string, typebotId: string) => { export const getAllResults = async (workspaceId: string, typebotId: string) => {
const results = [] const results = []
let hasMore = true let hasMore = true

View File

@ -3604,9 +3604,9 @@
set-cookie-parser "^2.4.6" set-cookie-parser "^2.4.6"
"@mswjs/interceptors@^0.15.1": "@mswjs/interceptors@^0.15.1":
version "0.15.1" version "0.15.3"
resolved "https://registry.yarnpkg.com/@mswjs/interceptors/-/interceptors-0.15.1.tgz#4a0009f56e51bc2cd3176f1507065c7d2f6c0d5e" resolved "https://registry.yarnpkg.com/@mswjs/interceptors/-/interceptors-0.15.3.tgz#bcd17b5d7558d4f598007a4bb383b42dc9264f8d"
integrity sha512-D5B+ZJNlfvBm6ZctAfRBdNJdCHYAe2Ix4My5qfbHV5WH+3lkt3mmsjiWJzEh5ZwGDauzY487TldI275If7DJVw== integrity sha512-GJ1qzBq82EQ3bwhsvw5nScbrLzOSI5H/TyB2CGd1K7dDqX58DJDLJHexiN+S5Ucvl6/84FjRdIysz0RxE/L8MA==
dependencies: dependencies:
"@open-draft/until" "^1.0.3" "@open-draft/until" "^1.0.3"
"@xmldom/xmldom" "^0.7.5" "@xmldom/xmldom" "^0.7.5"