fix(audit): documenso — M2 (reservation EOI-milestone pollution), L11 (v2 numericId GET fallback), L12 (API URL normalize/validate), L13 (event dedup)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-02 12:59:07 +02:00
parent 37ffb2c3b4
commit 4084029962
4 changed files with 156 additions and 36 deletions

View File

@@ -10,6 +10,7 @@
import { env } from '@/lib/env';
import { normalizeBrandingUrl } from '@/lib/branding/url';
import { getSetting } from '@/lib/services/settings.service';
import { normalizeDocumensoApiUrl } from '@/lib/validators/settings';
// ─── Setting key constants ───────────────────────────────────────────────────
@@ -430,18 +431,28 @@ export async function getPortDocumensoConfig(portId: string): Promise<PortDocume
readSetting<string>(SETTING_KEYS.publicSiteUrl, portId),
]);
// Defense-in-depth: normalize any persisted Documenso API URL override at
// read time. Writes are normalized + URL-validated by upsertSettingSchema,
// but values stored before that validation landed (or seeded directly) may
// still carry a `/api/v1`|`/api/v2` suffix that would double-path requests
// (documensoFetch appends `/api/vN/...` to this base). Strip it here too.
const normalizedApiUrl =
typeof apiUrl === 'string' && apiUrl.length > 0
? (normalizeDocumensoApiUrl(apiUrl) as string)
: apiUrl;
// Determine the resolution source for the two credentials. Used by
// the documenso client to enrich 401/403 error messages so operators
// can tell at a glance whether the failing key is per-port or env.
type Source = 'port' | 'global' | 'env' | 'default' | 'none';
const apiUrlSource: Source = apiUrl ? 'port' : env.DOCUMENSO_API_URL ? 'env' : 'none';
const apiUrlSource: Source = normalizedApiUrl ? 'port' : env.DOCUMENSO_API_URL ? 'env' : 'none';
const apiKeySource: Source = apiKey ? 'port' : env.DOCUMENSO_API_KEY ? 'env' : 'none';
return {
// Env values are now optional (admin is canonical). Empty / zero
// defaults let consumers proceed and fail at the actual API call with
// a clearer "not configured" error rather than crashing at type-check.
apiUrl: apiUrl ?? env.DOCUMENSO_API_URL ?? '',
apiUrl: normalizedApiUrl ?? env.DOCUMENSO_API_URL ?? '',
apiKey: apiKey ?? env.DOCUMENSO_API_KEY ?? '',
apiVersion: apiVersion ?? env.DOCUMENSO_API_VERSION,
apiKeySource,