diff --git a/src/backend/compare/compare.ts b/src/backend/compare/compare.ts index db4f23fa..4b82e24b 100644 --- a/src/backend/compare/compare.ts +++ b/src/backend/compare/compare.ts @@ -14,23 +14,28 @@ import { respondProjectNotFound } from '../common/standard-responses.js'; import { refreshSecret } from '../util.js'; import { deleteReport, renderCompare } from './report.js'; import type { TimelineRequest } from '../../shared/api.js'; +import { getNumberOrError } from '../request-check.js'; +import { log } from '../logging.js'; export async function getProfileAsJson( ctx: ParameterizedContext, db: Database ): Promise { + ctx.type = 'application/json'; + + const runId = getNumberOrError(ctx, 'runId'); + if (runId === null) { + log.error((ctx.body as any).error); + return; + } + const start = startRequest(); - ctx.body = await getProfile( - Number(ctx.params.runId), - ctx.params.commitId, - db - ); + ctx.body = await getProfile(runId, ctx.params.commitId, db); if (ctx.body === undefined) { ctx.status = 404; ctx.body = {}; } - ctx.type = 'application/json'; completeRequestAndHandlePromise(start, db, 'get-profiles'); } @@ -70,17 +75,24 @@ export async function getMeasurementsAsJson( ctx: ParameterizedContext, db: Database ): Promise { + ctx.type = 'application/json'; + + const runId = getNumberOrError(ctx, 'runId'); + if (runId === null) { + log.error((ctx.body as any).error); + return; + } + const start = startRequest(); ctx.body = await getMeasurements( ctx.params.projectSlug, - Number(ctx.params.runId), + runId, ctx.params.baseId, ctx.params.changeId, db ); - ctx.type = 'application/json'; completeRequestAndHandlePromise(start, db, 'get-measurements'); } diff --git a/src/backend/main/main.ts b/src/backend/main/main.ts index c0287596..95317faa 100644 --- a/src/backend/main/main.ts +++ b/src/backend/main/main.ts @@ -15,6 +15,8 @@ import { import type { AllResults } from '../../shared/api.js'; import { Database } from '../db/db.js'; import { TimedCacheValidity } from '../db/timed-cache-validity.js'; +import { getNumberOrError } from '../request-check.js'; +import { log } from '../logging.js'; const mainTpl = prepareTemplate(robustPath('backend/main/index.html'), false); @@ -34,11 +36,16 @@ export async function getLast100MeasurementsAsJson( ctx: ParameterizedContext, db: Database ): Promise { - const start = startRequest(); - - ctx.body = await getLast100Measurements(Number(ctx.params.projectId), db); ctx.type = 'application/json'; + const projectId = getNumberOrError(ctx, 'projectId'); + if (projectId === null) { + log.error((ctx.body as any).error); + return; + } + + const start = startRequest(); + ctx.body = await getLast100Measurements(projectId, db); completeRequestAndHandlePromise(start, db, 'get-results'); } @@ -166,8 +173,15 @@ export async function getChangesAsJson( ctx: ParameterizedContext, db: Database ): Promise { - ctx.body = await getChanges(Number(ctx.params.projectId), db); ctx.type = 'application/json'; + + const projectId = getNumberOrError(ctx, 'projectId'); + if (projectId === null) { + log.error((ctx.body as any).error); + return; + } + + ctx.body = await getChanges(projectId, db); } export async function getChanges( diff --git a/src/backend/project/data-export.ts b/src/backend/project/data-export.ts index d447afbd..522e13f7 100644 --- a/src/backend/project/data-export.ts +++ b/src/backend/project/data-export.ts @@ -7,6 +7,7 @@ import { dbConfig, siteConfig, storeJsonGzip } from '../util.js'; import { log } from '../logging.js'; import { Database } from '../db/db.js'; import { ParameterizedContext } from 'koa'; +import { getNumberOrError } from '../request-check.js'; const expDataPreparation = new Map(); @@ -98,8 +99,15 @@ export async function getAvailableDataAsJson( ctx: ParameterizedContext, db: Database ): Promise { - ctx.body = await getDataOverview(Number(ctx.params.projectId), db); ctx.type = 'application/json'; + + const projectId = getNumberOrError(ctx, 'projectId'); + if (projectId === null) { + log.error((ctx.body as any).error); + return; + } + + ctx.body = await getDataOverview(projectId, db); } export async function getDataOverview( diff --git a/src/backend/request-check.ts b/src/backend/request-check.ts new file mode 100644 index 00000000..90b0d578 --- /dev/null +++ b/src/backend/request-check.ts @@ -0,0 +1,19 @@ +import { ParameterizedContext } from 'koa'; + +export function getNumberOrError( + ctx: ParameterizedContext, + paramName: string +): number | null { + const value = Number(ctx.params[paramName]); + + if (isNaN(value)) { + ctx.status = 400; + ctx.body = { + error: `Invalid ${paramName} provided. Received "${ctx.params.runId}".` + }; + + return null; + } + + return value; +} diff --git a/src/backend/timeline/timeline.ts b/src/backend/timeline/timeline.ts index aa15f1cd..b3aa2ad1 100644 --- a/src/backend/timeline/timeline.ts +++ b/src/backend/timeline/timeline.ts @@ -7,6 +7,8 @@ import { prepareTemplate } from '../templates.js'; import { TimelineSuite } from '../../shared/api.js'; import { Database } from '../db/db.js'; import { robustPath } from '../util.js'; +import { getNumberOrError } from '../request-check.js'; +import { log } from '../logging.js'; const timelineTpl = prepareTemplate( robustPath('backend/timeline/timeline.html'), @@ -17,14 +19,24 @@ export async function getTimelineAsJson( ctx: ParameterizedContext, db: Database ): Promise { - ctx.body = await db.getTimelineForRun( - Number(ctx.params.projectId), - Number(ctx.params.runId) - ); + ctx.type = 'application/json'; + + const projectId = getNumberOrError(ctx, 'projectId'); + if (projectId === null) { + log.error((ctx.body as any).error); + return; + } + + const runId = getNumberOrError(ctx, 'runId'); + if (runId === null) { + log.error((ctx.body as any).error); + return; + } + + ctx.body = await db.getTimelineForRun(projectId, runId); if (ctx.body === null) { ctx.status = 500; } - ctx.type = 'application/json'; } /**