import path from 'node:path'; import { assertApiCredentialId, assertProjectPolicyProjectId, normalizeProjectPolicySubject, readPrivateLocalCommandFile, type SecuritySubject, } from './codecAuthority'; import { LocalIdentityCredentialCommandConfigurationError, type BaseInspectionRequest, type BaseMutationRequest, type BaseTargetMutationRequest, type LocalIdentityCredentialCommand, type LocalIdentityCredentialCommandOptions, } from './contracts'; export const MAX_PATH_BYTES = 4096; export const MAX_VERSION = 2_147_483_647; export const MIN_CREDENTIAL_LIFETIME_MS = 60_000; export const MAX_CREDENTIAL_LIFETIME_MS = 2 * 365 * 24 * 60 * 60 * 1000; export const UUID_V4_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/; export const REQUEST_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/; export const DIGEST_PATTERN = /^[0-9a-f]{64}$/; export function exactObject( value: unknown, expectedKeys: readonly string[], label: string, ): asserts value is Record { if (!value || typeof value !== 'object' || Array.isArray(value)) { throw new LocalIdentityCredentialCommandConfigurationError( `${label} must be an object`, ); } const actual = Object.keys(value).sort(); const expected = [...expectedKeys].sort(); if ( actual.length !== expected.length || actual.some((key, index) => key !== expected[index]) ) { throw new LocalIdentityCredentialCommandConfigurationError( `${label} shape is invalid`, ); } } export function boundedPath(value: unknown, label: string): string { if ( typeof value !== 'string' || !path.isAbsolute(value) || path.parse(value).root === value || path.normalize(value) !== value || value.includes('\0') || Buffer.byteLength(value, 'utf8') > MAX_PATH_BYTES ) { throw new LocalIdentityCredentialCommandConfigurationError( `${label} must be a normalized bounded absolute non-root path`, ); } return value; } export function descendant( root: string, candidate: string, label: string, ): void { const relative = path.relative(root, candidate); if ( relative.length === 0 || relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative) ) { throw new LocalIdentityCredentialCommandConfigurationError( `${label} must be a descendant of deploymentRoot`, ); } } export function requiresDelivery( operation: LocalIdentityCredentialCommand['operation'], ) { return ( operation === 'credential.issue' || operation === 'credential.rotate' || operation === 'credential.delivery.acknowledge' ); } export function normalizeOptions( value: unknown, operation: LocalIdentityCredentialCommand['operation'], ): Readonly { const hasBusyTimeout = !!value && typeof value === 'object' && !Array.isArray(value) && Object.hasOwn(value, 'busyTimeoutMs'); const deliveryRequired = requiresDelivery(operation); exactObject( value, [ 'deploymentRoot', 'databasePath', 'profile', 'ownerPepperKeyringDirectory', 'credentialFilePath', ...(deliveryRequired ? ['credentialDeliveryDirectory'] : []), ...(hasBusyTimeout ? ['busyTimeoutMs'] : []), ], 'options', ); const deploymentRoot = boundedPath(value.deploymentRoot, 'deploymentRoot'); const databasePath = boundedPath(value.databasePath, 'databasePath'); const ownerPepperKeyringDirectory = boundedPath( value.ownerPepperKeyringDirectory, 'ownerPepperKeyringDirectory', ); const credentialFilePath = boundedPath( value.credentialFilePath, 'credentialFilePath', ); for (const [label, candidate] of [ ['databasePath', databasePath], ['ownerPepperKeyringDirectory', ownerPepperKeyringDirectory], ['credentialFilePath', credentialFilePath], ] as const) { descendant(deploymentRoot, candidate, label); } let credentialDeliveryDirectory: string | undefined; if (deliveryRequired) { credentialDeliveryDirectory = boundedPath( value.credentialDeliveryDirectory, 'credentialDeliveryDirectory', ); descendant( deploymentRoot, credentialDeliveryDirectory, 'credentialDeliveryDirectory', ); if ( credentialDeliveryDirectory === path.dirname(databasePath) || credentialDeliveryDirectory === ownerPepperKeyringDirectory ) { throw new LocalIdentityCredentialCommandConfigurationError( 'credentialDeliveryDirectory must not share database or keyring storage', ); } } if (value.profile !== 'edge' && value.profile !== 'standalone') { throw new LocalIdentityCredentialCommandConfigurationError( 'profile must be edge or standalone', ); } if ( value.busyTimeoutMs !== undefined && (!Number.isSafeInteger(value.busyTimeoutMs) || (value.busyTimeoutMs as number) < 100 || (value.busyTimeoutMs as number) > 30_000) ) { throw new LocalIdentityCredentialCommandConfigurationError( 'busyTimeoutMs is invalid', ); } return Object.freeze({ deploymentRoot, databasePath, profile: value.profile, ownerPepperKeyringDirectory, credentialFilePath, ...(credentialDeliveryDirectory === undefined ? {} : { credentialDeliveryDirectory }), ...(value.busyTimeoutMs === undefined ? {} : { busyTimeoutMs: value.busyTimeoutMs as number }), }); } export function normalizeCommonRequest( value: Record, ): Readonly { try { assertProjectPolicyProjectId(value.projectId as string); } catch (error) { throw new LocalIdentityCredentialCommandConfigurationError( 'projectId is invalid', error, ); } if ( typeof value.mutationId !== 'string' || !UUID_V4_PATTERN.test(value.mutationId) || typeof value.failureAuditEventId !== 'string' || !UUID_V4_PATTERN.test(value.failureAuditEventId) || value.failureAuditEventId === value.mutationId || typeof value.requestId !== 'string' || !REQUEST_ID_PATTERN.test(value.requestId) ) { throw new LocalIdentityCredentialCommandConfigurationError( 'mutation or request identity is invalid', ); } return Object.freeze({ projectId: value.projectId as string, mutationId: value.mutationId, requestId: value.requestId, failureAuditEventId: value.failureAuditEventId, }); } export function normalizeInspectionCommonRequest( value: Record, ): Readonly { try { assertProjectPolicyProjectId(value.projectId as string); } catch (error) { throw new LocalIdentityCredentialCommandConfigurationError( 'projectId is invalid', error, ); } if ( typeof value.auditEventId !== 'string' || !UUID_V4_PATTERN.test(value.auditEventId) || typeof value.requestId !== 'string' || !REQUEST_ID_PATTERN.test(value.requestId) ) { throw new LocalIdentityCredentialCommandConfigurationError( 'audit or request identity is invalid', ); } return Object.freeze({ projectId: value.projectId as string, requestId: value.requestId, auditEventId: value.auditEventId, }); } export function normalizeInspectionTarget( value: unknown, ): Readonly { let target: Readonly; try { target = normalizeProjectPolicySubject(value as SecuritySubject); } catch (error) { throw new LocalIdentityCredentialCommandConfigurationError( 'target is invalid', error, ); } if (!['user', 'api_app', 'mcp_client', 'agent'].includes(target.type)) { throw new LocalIdentityCredentialCommandConfigurationError( 'target is invalid', ); } return target; } export function normalizeTargetRequest( value: Record, ): Readonly { const common = normalizeCommonRequest(value); let target: Readonly; try { target = normalizeProjectPolicySubject(value.target as SecuritySubject); } catch (error) { throw new LocalIdentityCredentialCommandConfigurationError( 'target is invalid', error, ); } if ( !['user', 'api_app', 'mcp_client', 'agent'].includes(target.type) || !Number.isSafeInteger(value.expectedCurrentVersion) || (value.expectedCurrentVersion as number) < 0 || (value.expectedCurrentVersion as number) >= MAX_VERSION ) { throw new LocalIdentityCredentialCommandConfigurationError( 'target or expectedCurrentVersion is invalid', ); } return Object.freeze({ ...common, target, expectedCurrentVersion: value.expectedCurrentVersion as number, }); } export function normalizeRequest( value: unknown, operation: LocalIdentityCredentialCommand['operation'], ): LocalIdentityCredentialCommand['request'] { const inspection = operation === 'identity.inspect' || operation === 'credential.inspect'; if (inspection) { const identityInspection = operation === 'identity.inspect'; exactObject( value, [ 'projectId', ...(identityInspection ? ['target'] : ['credentialId']), 'requestId', 'auditEventId', ], 'request', ); const common = normalizeInspectionCommonRequest(value); if (identityInspection) { return Object.freeze({ ...common, target: normalizeInspectionTarget(value.target), }); } try { assertApiCredentialId(value.credentialId as string); } catch (error) { throw new LocalIdentityCredentialCommandConfigurationError( 'credentialId is invalid', error, ); } return Object.freeze({ ...common, credentialId: value.credentialId as string, }); } const identity = operation.startsWith('identity.'); const activeCredential = operation === 'credential.issue' || operation === 'credential.rotate'; const revokeCredential = operation === 'credential.revoke'; const acknowledge = operation === 'credential.delivery.acknowledge'; exactObject( value, [ 'projectId', ...(acknowledge ? [] : ['target', 'expectedCurrentVersion']), ...(activeCredential || revokeCredential ? ['credentialId'] : []), ...(activeCredential ? ['lifetimeMs'] : []), ...(acknowledge ? ['credentialMutationId', 'expectedDeliveryDigest'] : []), 'mutationId', 'requestId', 'failureAuditEventId', ], 'request', ); if (acknowledge) { const common = normalizeCommonRequest(value); if ( typeof value.credentialMutationId !== 'string' || !UUID_V4_PATTERN.test(value.credentialMutationId) || value.credentialMutationId === common.mutationId || typeof value.expectedDeliveryDigest !== 'string' || !DIGEST_PATTERN.test(value.expectedDeliveryDigest) ) { throw new LocalIdentityCredentialCommandConfigurationError( 'delivery acknowledgement is invalid', ); } return Object.freeze({ ...common, credentialMutationId: value.credentialMutationId, expectedDeliveryDigest: value.expectedDeliveryDigest, }); } const target = normalizeTargetRequest(value); if (identity) return target; try { assertApiCredentialId(value.credentialId as string); } catch (error) { throw new LocalIdentityCredentialCommandConfigurationError( 'credentialId is invalid', error, ); } if ( activeCredential && (!Number.isSafeInteger(value.lifetimeMs) || (value.lifetimeMs as number) < MIN_CREDENTIAL_LIFETIME_MS || (value.lifetimeMs as number) > MAX_CREDENTIAL_LIFETIME_MS) ) { throw new LocalIdentityCredentialCommandConfigurationError( 'lifetimeMs is invalid', ); } return Object.freeze({ ...target, credentialId: value.credentialId as string, ...(activeCredential ? { lifetimeMs: value.lifetimeMs as number } : {}), }); } export function normalizeCommand( value: unknown, ): Readonly { exactObject( value, ['schemaVersion', 'operation', 'options', 'request'], 'command', ); const operations: readonly LocalIdentityCredentialCommand['operation'][] = [ 'identity.inspect', 'identity.register', 'identity.enable', 'identity.disable', 'credential.inspect', 'credential.issue', 'credential.rotate', 'credential.revoke', 'credential.delivery.acknowledge', ]; if ( value.schemaVersion !== 1 || typeof value.operation !== 'string' || !operations.includes( value.operation as LocalIdentityCredentialCommand['operation'], ) ) { throw new LocalIdentityCredentialCommandConfigurationError( 'command version or operation is invalid', ); } const operation = value.operation as LocalIdentityCredentialCommand['operation']; return Object.freeze({ schemaVersion: 1, operation, options: normalizeOptions(value.options, operation), request: normalizeRequest(value.request, operation), } as LocalIdentityCredentialCommand); } export function readCommandFile( candidatePath: string, ): Readonly { try { return normalizeCommand(readPrivateLocalCommandFile(candidatePath)); } catch (error) { if (error instanceof LocalIdentityCredentialCommandConfigurationError) { throw error; } throw new LocalIdentityCredentialCommandConfigurationError( 'command file cannot be read', error, ); } }