Skip to content

Commit 9aece3d

Browse files
committed
fix(validation): enforce strict OpenAPI-normalized payload parsing
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
1 parent ee1d77d commit 9aece3d

File tree

1 file changed

+246
-5
lines changed

1 file changed

+246
-5
lines changed

src/views/Validation.vue

Lines changed: 246 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -217,29 +217,236 @@ function toValidationStatus(value: unknown): ValidationStatus | null {
217217
return null
218218
}
219219
220+
function toSignerStatus(value: unknown): SignerDetailRecord['status'] | null {
221+
const normalizedValue = toNumber(value)
222+
if (normalizedValue === 0 || normalizedValue === 1 || normalizedValue === 2) {
223+
return normalizedValue
224+
}
225+
return null
226+
}
227+
228+
function toStringOrNull(value: unknown): string | null {
229+
return typeof value === 'string' ? value : null
230+
}
231+
232+
function isNullableString(value: unknown): value is string | null {
233+
return value === null || typeof value === 'string'
234+
}
235+
236+
function normalizeRequestedBy(value: unknown): ValidationFileRecord['requested_by'] | null {
237+
if (!isRecord(value)) {
238+
return null
239+
}
240+
const userId = toStringOrNull(value.userId)
241+
const displayName = toStringOrNull(value.displayName)
242+
if (!userId || !displayName) {
243+
return null
244+
}
245+
return {
246+
userId,
247+
displayName,
248+
}
249+
}
250+
251+
function normalizeValidationMetadata(value: unknown): ValidationFileRecord['metadata'] | undefined {
252+
if (!isRecord(value)) {
253+
return undefined
254+
}
255+
const extension = toStringOrNull(value.extension)
256+
const p = toNumber(value.p)
257+
if (!extension || p === null) {
258+
return undefined
259+
}
260+
return {
261+
extension,
262+
p,
263+
}
264+
}
265+
266+
function normalizeValidationSettings(value: unknown): ValidationFileRecord['settings'] | undefined {
267+
if (!isRecord(value)) {
268+
return undefined
269+
}
270+
if (
271+
typeof value.canSign !== 'boolean'
272+
|| typeof value.canRequestSign !== 'boolean'
273+
|| typeof value.phoneNumber !== 'string'
274+
|| typeof value.hasSignatureFile !== 'boolean'
275+
|| typeof value.needIdentificationDocuments !== 'boolean'
276+
|| typeof value.identificationDocumentsWaitingApproval !== 'boolean'
277+
) {
278+
return undefined
279+
}
280+
return {
281+
canSign: value.canSign,
282+
canRequestSign: value.canRequestSign,
283+
phoneNumber: value.phoneNumber,
284+
hasSignatureFile: value.hasSignatureFile,
285+
needIdentificationDocuments: value.needIdentificationDocuments,
286+
identificationDocumentsWaitingApproval: value.identificationDocumentsWaitingApproval,
287+
isApprover: typeof value.isApprover === 'boolean' ? value.isApprover : undefined,
288+
}
289+
}
290+
220291
function normalizeValidationSigner(signer: unknown): SignerDetailRecord | null {
221292
if (!isRecord(signer)) {
222293
return null
223294
}
224-
return { ...signer } as SignerDetailRecord
295+
296+
const signRequestId = toNumber(signer.signRequestId)
297+
const displayName = toStringOrNull(signer.displayName)
298+
const email = toStringOrNull(signer.email)
299+
const signed = isNullableString(signer.signed) ? signer.signed : null
300+
const status = toSignerStatus(signer.status)
301+
const statusText = toStringOrNull(signer.statusText)
302+
const description = isNullableString(signer.description) ? signer.description : null
303+
const requestSignDate = toStringOrNull(signer.request_sign_date)
304+
const me = typeof signer.me === 'boolean' ? signer.me : null
305+
const visibleElements = Array.isArray(signer.visibleElements) ? signer.visibleElements : null
306+
307+
if (
308+
signRequestId === null
309+
|| !displayName
310+
|| !email
311+
|| status === null
312+
|| !statusText
313+
|| !requestSignDate
314+
|| me === null
315+
|| !visibleElements
316+
) {
317+
return null
318+
}
319+
320+
const normalizedSigner: SignerDetailRecord = {
321+
signRequestId,
322+
displayName,
323+
email,
324+
signed,
325+
status,
326+
statusText,
327+
description,
328+
request_sign_date: requestSignDate,
329+
me,
330+
visibleElements,
331+
}
332+
333+
const subject = toStringOrNull(signer.subject)
334+
if (subject) {
335+
normalizedSigner.subject = subject
336+
}
337+
const validFrom = toNumber(signer.valid_from)
338+
if (validFrom !== null) {
339+
normalizedSigner.valid_from = validFrom
340+
}
341+
const validTo = toNumber(signer.valid_to)
342+
if (validTo !== null) {
343+
normalizedSigner.valid_to = validTo
344+
}
345+
const remoteAddress = toStringOrNull(signer.remote_address)
346+
if (remoteAddress) {
347+
normalizedSigner.remote_address = remoteAddress
348+
}
349+
const userAgent = toStringOrNull(signer.user_agent)
350+
if (userAgent) {
351+
normalizedSigner.user_agent = userAgent
352+
}
353+
const userId = toStringOrNull(signer.userId)
354+
if (userId) {
355+
normalizedSigner.userId = userId
356+
}
357+
const signDate = isNullableString(signer.sign_date) ? signer.sign_date : undefined
358+
if (signDate !== undefined) {
359+
normalizedSigner.sign_date = signDate
360+
}
361+
const signRequestUuid = toStringOrNull(signer.sign_request_uuid)
362+
if (signRequestUuid) {
363+
normalizedSigner.sign_request_uuid = signRequestUuid
364+
}
365+
const hashAlgorithm = toStringOrNull(signer.hash_algorithm)
366+
if (hashAlgorithm) {
367+
normalizedSigner.hash_algorithm = hashAlgorithm
368+
}
369+
const signingOrder = toNumber(signer.signingOrder)
370+
if (signingOrder !== null) {
371+
normalizedSigner.signingOrder = signingOrder
372+
}
373+
374+
return normalizedSigner
225375
}
226376
227377
function normalizeValidationChildFile(file: unknown): ValidatedChildFileRecord | null {
228378
if (!isRecord(file)) {
229379
return null
230380
}
231-
return { ...file } as ValidatedChildFileRecord
381+
382+
const id = toNumber(file.id)
383+
const uuid = toStringOrNull(file.uuid)
384+
const name = toStringOrNull(file.name)
385+
const status = toValidationStatus(file.status)
386+
const statusText = toStringOrNull(file.statusText)
387+
const nodeId = toNumber(file.nodeId)
388+
const size = toNumber(file.size)
389+
const fileUrl = toStringOrNull(file.file)
390+
const metadata = isRecord(file.metadata) ? file.metadata : null
391+
const metadataExtension = metadata ? toStringOrNull(metadata.extension) : null
392+
const metadataPageCount = metadata ? toNumber(metadata.p) : null
393+
const signers = Array.isArray(file.signers) ? file.signers : null
394+
395+
if (
396+
id === null
397+
|| !uuid
398+
|| !name
399+
|| status === null
400+
|| !statusText
401+
|| nodeId === null
402+
|| size === null
403+
|| !fileUrl
404+
|| !metadata
405+
|| !metadataExtension
406+
|| metadataPageCount === null
407+
|| !signers
408+
) {
409+
return null
410+
}
411+
412+
return {
413+
id,
414+
uuid,
415+
name,
416+
status,
417+
statusText,
418+
nodeId,
419+
totalPages: toNumber(file.totalPages) ?? 0,
420+
size,
421+
pdfVersion: toStringOrNull(file.pdfVersion) ?? '',
422+
signers,
423+
file: fileUrl,
424+
metadata: {
425+
extension: metadataExtension,
426+
p: metadataPageCount,
427+
},
428+
}
232429
}
233430
234431
function normalizeValidationDocument(data: unknown): ValidationFileRecord | null {
235432
if (!isRecord(data)) {
236433
return null
237434
}
238435
436+
const id = toNumber(data.id)
239437
const uuid = typeof data.uuid === 'string' ? data.uuid : null
240438
const name = typeof data.name === 'string' ? data.name : null
241439
const nodeId = toNumber(data.nodeId)
242440
const status = toValidationStatus(data.status)
441+
const statusText = toStringOrNull(data.statusText)
442+
const signatureFlow = toNumber(data.signatureFlow)
443+
const docmdpLevel = toNumber(data.docmdpLevel)
444+
const filesCount = toNumber(data.filesCount)
445+
const totalPages = toNumber(data.totalPages)
446+
const size = toNumber(data.size)
447+
const pdfVersion = toStringOrNull(data.pdfVersion)
448+
const createdAt = toStringOrNull(data.created_at)
449+
const requestedBy = normalizeRequestedBy(data.requested_by)
243450
const files = Array.isArray(data.files)
244451
? data.files
245452
.map(normalizeValidationChildFile)
@@ -249,26 +456,60 @@ function normalizeValidationDocument(data: unknown): ValidationFileRecord | null
249456
? data.signers
250457
.map(normalizeValidationSigner)
251458
.filter((signer): signer is SignerDetailRecord => signer !== null)
252-
: undefined
459+
: []
253460
const nodeType = data.nodeType === 'envelope'
254461
? 'envelope'
255462
: data.nodeType === 'file'
256463
? 'file'
257464
: null
258465
259-
if (!uuid || !name || nodeId === null || status === null || nodeType === null) {
466+
if (
467+
id === null
468+
|| !uuid
469+
|| !name
470+
|| nodeId === null
471+
|| status === null
472+
|| !statusText
473+
|| nodeType === null
474+
|| signatureFlow === null
475+
|| docmdpLevel === null
476+
|| filesCount === null
477+
|| totalPages === null
478+
|| size === null
479+
|| !pdfVersion
480+
|| !createdAt
481+
|| !requestedBy
482+
) {
260483
return null
261484
}
262485
263486
return {
264-
...(data as ValidationFileRecord),
487+
id,
265488
uuid,
266489
name,
490+
statusText,
267491
nodeId,
268492
status,
269493
nodeType,
494+
signatureFlow,
495+
docmdpLevel,
496+
filesCount,
270497
files,
498+
totalPages,
499+
size,
500+
pdfVersion,
501+
created_at: createdAt,
502+
requested_by: requestedBy,
503+
file: toStringOrNull(data.file) ?? undefined,
504+
url: toStringOrNull(data.url) ?? undefined,
505+
mime: toStringOrNull(data.mime) ?? undefined,
506+
metadata: normalizeValidationMetadata(data.metadata),
507+
signersCount: toNumber(data.signersCount) ?? undefined,
271508
signers,
509+
settings: normalizeValidationSettings(data.settings),
510+
messages: Array.isArray(data.messages) ? data.messages : undefined,
511+
visibleElements: Array.isArray(data.visibleElements) ? data.visibleElements : undefined,
512+
pages: Array.isArray(data.pages) ? data.pages : undefined,
272513
}
273514
}
274515

0 commit comments

Comments
 (0)