refactor(crypto): use Zod safeParse for summary.json validation instead of manual checks
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
||||
import { ORPCError } from '@orpc/server'
|
||||
import type { JSZipObject } from 'jszip'
|
||||
import JSZip from 'jszip'
|
||||
import { z } from 'zod'
|
||||
import { getUxConfig } from '@/server/ux-config'
|
||||
import { db } from '../middlewares'
|
||||
import { os } from '../server'
|
||||
@@ -20,6 +21,14 @@ interface ZipFileItem {
|
||||
bytes: Uint8Array
|
||||
}
|
||||
|
||||
const summaryPayloadSchema = z
|
||||
.object({
|
||||
taskId: z.string().min(1, 'summary.json must contain a non-empty taskId'),
|
||||
checkId: z.string().optional(),
|
||||
inspectionId: z.string().optional(),
|
||||
})
|
||||
.passthrough()
|
||||
|
||||
const MAX_RAW_ZIP_BYTES = 50 * 1024 * 1024
|
||||
const MAX_SINGLE_FILE_BYTES = 20 * 1024 * 1024
|
||||
const MAX_TOTAL_UNCOMPRESSED_BYTES = 60 * 1024 * 1024
|
||||
@@ -165,7 +174,7 @@ export const signAndPackReport = os.crypto.signAndPackReport.use(db).handler(asy
|
||||
|
||||
const zipFiles = await listSafeZipFiles(rawZip)
|
||||
|
||||
// Extract and parse summary.json from the ZIP
|
||||
// Extract and validate summary.json from the ZIP
|
||||
const summaryFile = zipFiles.find((f) => f.name === 'summary.json')
|
||||
if (!summaryFile) {
|
||||
throw new ORPCError('BAD_REQUEST', {
|
||||
@@ -173,24 +182,25 @@ export const signAndPackReport = os.crypto.signAndPackReport.use(db).handler(asy
|
||||
})
|
||||
}
|
||||
|
||||
let summaryPayload: Record<string, unknown>
|
||||
let rawJson: unknown
|
||||
try {
|
||||
summaryPayload = JSON.parse(Buffer.from(summaryFile.bytes).toString('utf-8'))
|
||||
rawJson = JSON.parse(Buffer.from(summaryFile.bytes).toString('utf-8'))
|
||||
} catch {
|
||||
throw new ORPCError('BAD_REQUEST', {
|
||||
message: 'summary.json in the ZIP is not valid JSON',
|
||||
})
|
||||
}
|
||||
|
||||
// Derive signingContext from summary.json fields
|
||||
const taskId = String(summaryPayload.taskId ?? '')
|
||||
const checkId = String(summaryPayload.checkId ?? summaryPayload.inspectionId ?? '')
|
||||
if (!taskId) {
|
||||
const parsed = summaryPayloadSchema.safeParse(rawJson)
|
||||
if (!parsed.success) {
|
||||
throw new ORPCError('BAD_REQUEST', {
|
||||
message: 'summary.json must contain a taskId field',
|
||||
message: `Invalid summary.json: ${z.prettifyError(parsed.error)}`,
|
||||
})
|
||||
}
|
||||
const signingContext = `${taskId}${checkId}`
|
||||
|
||||
const summaryPayload = parsed.data
|
||||
const checkId = summaryPayload.checkId ?? summaryPayload.inspectionId ?? ''
|
||||
const signingContext = `${summaryPayload.taskId}${checkId}`
|
||||
|
||||
// Compute device signature
|
||||
const ikm = config.licence + config.fingerprint
|
||||
|
||||
Reference in New Issue
Block a user