mirror of
https://github.com/documenso/documenso.git
synced 2026-08-27 00:32:29 +10:00
fix: wip
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
import type { Recipient } from '@prisma/client';
|
||||
|
||||
import { AppError, AppErrorCode } from '../../errors/app-error';
|
||||
import { isTspEnvelope } from '../../types/signature-level';
|
||||
import { effectiveOrder } from '../../utils/recipient-groups';
|
||||
import { isCcRecipient } from '../../utils/recipients';
|
||||
|
||||
type AssertCompatibleRecipientGroupingOptions = {
|
||||
signatureLevel: string;
|
||||
recipients: Array<Pick<Recipient, 'role'> & { signingOrder?: number | null }>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Reject recipient signing groups on AES/QES envelopes.
|
||||
*
|
||||
* A "group" is two or more signing recipients sharing a signing step, which
|
||||
* they may then complete in any order — including at the same time. That is
|
||||
* parallel signing scoped to one step, so it breaks the same per-recipient
|
||||
* `/ByteRange` invariant that {@link assertCompatibleSigningOrder} exists to
|
||||
* protect: each TSP signature must cover the exact bytes it was applied to,
|
||||
* and concurrent incremental updates over one base state cannot all verify.
|
||||
*
|
||||
* Note this is not caught by the `signingOrder = PARALLEL` guard: groups are
|
||||
* expressed as duplicate `Recipient.signingOrder` values on a document whose
|
||||
* `documentMeta.signingOrder` is SEQUENTIAL.
|
||||
*
|
||||
* Recipients sharing a step are detected by {@link effectiveOrder}, so an
|
||||
* absent signing order counts too — every unordered recipient lands in the
|
||||
* same tail step and would sign in parallel.
|
||||
*
|
||||
* CC recipients are ignored: they never sign, and their signing order carries
|
||||
* no meaning anywhere else.
|
||||
*
|
||||
* SES envelopes pass through unchanged — signing groups are an SES feature.
|
||||
*
|
||||
* Schema-layer guard. {@link sendDocument} re-checks at distribution time as a
|
||||
* defence-in-depth backstop.
|
||||
*/
|
||||
export const assertCompatibleRecipientGrouping = ({
|
||||
signatureLevel,
|
||||
recipients,
|
||||
}: AssertCompatibleRecipientGroupingOptions): void => {
|
||||
if (!isTspEnvelope({ signatureLevel })) {
|
||||
return;
|
||||
}
|
||||
|
||||
const seenOrders = new Set<number>();
|
||||
|
||||
for (const recipient of recipients) {
|
||||
if (isCcRecipient(recipient)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const order = effectiveOrder(recipient);
|
||||
|
||||
if (seenOrders.has(order)) {
|
||||
throw new AppError(AppErrorCode.INVALID_BODY, {
|
||||
message: `Envelopes signed at '${signatureLevel}' cannot place two recipients in the same signing step — a signing group is parallel signing within one step, which breaks the per-recipient /ByteRange invariant TSP signatures rely on. Give every signing recipient a distinct signingOrder.`,
|
||||
});
|
||||
}
|
||||
|
||||
seenOrders.add(order);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user