import { normalizeApprovalRequestRecord } from './approvedAction'; import { normalizeApprovalDetailPreview, type ApprovalRequestDetail, type ApprovalRequestDetailSource, } from './approvalDiscovery'; import { ProjectPolicyEngine } from '../security/project-policy/projectPolicy'; import { normalizeSecurityPrincipal, type SecurityPolicyFence, type SecurityPrincipal, } from '../security/security'; import type { SecurityAuditSink } from '../security/audit/securityAudit'; const IDENTIFIER_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/; 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}$/; const STRONG_HUMAN_ASSURANCES = new Set([ 'multi_factor', 'hardware', 'local_console', ]); export interface ApprovalInspectionRequest { readonly projectId: string; readonly approvalRequestId: string; readonly auditEventId: string; readonly requestId: string; readonly principal: Readonly; } export interface ApprovalInspectionService { inspect( request: ApprovalInspectionRequest, ): Promise | null>; } export interface ApprovalInspectionServiceOptions { readonly source: Pick; readonly policy: Pick; readonly audit: Pick; readonly confirmAuthorization?: () => void | Promise; readonly now?: () => number; } export class ApprovalInspectionRequestError extends TypeError { readonly code = 'APPROVAL_INSPECTION_REQUEST_INVALID'; constructor(message: string) { super(`Approval inspection request is invalid: ${message}`); this.name = 'ApprovalInspectionRequestError'; } } export class ApprovalInspectionAuthorizationError extends Error { readonly code = 'APPROVAL_INSPECTION_AUTHORIZATION_REJECTED'; constructor() { super('Approval inspection authorization was rejected'); this.name = 'ApprovalInspectionAuthorizationError'; } } export class ApprovalInspectionUnavailableError extends Error { readonly code = 'APPROVAL_INSPECTION_UNAVAILABLE'; constructor(options?: ErrorOptions) { super('Approval inspection authority is unavailable', options); this.name = 'ApprovalInspectionUnavailableError'; } } function exactObject( value: unknown, expected: readonly string[], label: string, ): asserts value is Record { if ( !value || typeof value !== 'object' || Array.isArray(value) || Object.keys(value).sort().join('\0') !== [...expected].sort().join('\0') ) { throw new ApprovalInspectionRequestError(`${label} shape is invalid`); } } function identifier(value: unknown, label: string): string { if (typeof value !== 'string' || !IDENTIFIER_PATTERN.test(value)) { throw new ApprovalInspectionRequestError(`${label} is invalid`); } return value; } function sameFence( left: Readonly, right: Readonly, ): boolean { return ( left.projectVersion === right.projectVersion && left.bindingVersion === right.bindingVersion ); } function observedTime(now: () => number): number { const value = now(); if (!Number.isSafeInteger(value) || value < 0) { throw new ApprovalInspectionUnavailableError(); } return value; } function normalizedRequest( value: ApprovalInspectionRequest, ): Readonly { exactObject( value, [ 'projectId', 'approvalRequestId', 'auditEventId', 'requestId', 'principal', ], 'request', ); if ( typeof value.auditEventId !== 'string' || !UUID_V4_PATTERN.test(value.auditEventId) ) { throw new ApprovalInspectionRequestError('auditEventId is invalid'); } return Object.freeze({ projectId: identifier(value.projectId, 'projectId'), approvalRequestId: identifier( value.approvalRequestId, 'approvalRequestId', ), auditEventId: value.auditEventId, requestId: identifier(value.requestId, 'requestId'), principal: value.principal, }); } export function createApprovalInspectionService( options: ApprovalInspectionServiceOptions, ): Readonly { exactObject( options, [ 'source', 'policy', 'audit', ...(options?.confirmAuthorization === undefined ? [] : ['confirmAuthorization']), ...(options?.now === undefined ? [] : ['now']), ], 'options', ); if ( typeof options.source?.getApprovalRequestDetail !== 'function' || typeof options.policy?.authorize !== 'function' || typeof options.audit?.record !== 'function' || (options.confirmAuthorization !== undefined && typeof options.confirmAuthorization !== 'function') || (options.now !== undefined && typeof options.now !== 'function') ) { throw new ApprovalInspectionRequestError('options are invalid'); } const now = options.now ?? Date.now; return Object.freeze({ async inspect(requestValue: ApprovalInspectionRequest) { const request = normalizedRequest(requestValue); const occurredAtMs = observedTime(now); let principal: Readonly; try { principal = normalizeSecurityPrincipal(request.principal, occurredAtMs); } catch { throw new ApprovalInspectionAuthorizationError(); } if ( principal.subject.type !== 'user' || !STRONG_HUMAN_ASSURANCES.has(principal.assurance) ) { throw new ApprovalInspectionAuthorizationError(); } let approvalRead; let artifactRead; try { [approvalRead, artifactRead] = await Promise.all([ options.policy.authorize( principal, request.projectId, 'approval.read', ), options.policy.authorize( principal, request.projectId, 'artifact.read', ), ]); } catch (error) { throw new ApprovalInspectionUnavailableError({ cause: error instanceof Error ? error : undefined, }); } if ( approvalRead.effect !== 'allow' || approvalRead.fence === null || artifactRead.effect !== 'allow' || artifactRead.fence === null || !sameFence(approvalRead.fence, artifactRead.fence) ) { throw new ApprovalInspectionAuthorizationError(); } await options.confirmAuthorization?.(); let detail: Readonly | null; try { const found = await options.source.getApprovalRequestDetail({ projectId: request.projectId, requestId: request.approvalRequestId, }); if (!found) { detail = null; } else { const approval = normalizeApprovalRequestRecord(found.request); if ( approval.projectId !== request.projectId || approval.id !== request.approvalRequestId ) { throw new ApprovalInspectionUnavailableError(); } detail = Object.freeze({ request: approval, preview: found.preview ? normalizeApprovalDetailPreview(found.preview) : null, }); } } catch (error) { if (error instanceof ApprovalInspectionUnavailableError) throw error; throw new ApprovalInspectionUnavailableError({ cause: error instanceof Error ? error : undefined, }); } try { await options.audit.record({ eventId: request.auditEventId, requestId: request.requestId, operationId: 'approval.inspect', projectId: request.projectId, subject: principal.subject, authenticationId: principal.authenticationId, outcome: 'allowed', reasons: Object.freeze(['human_approval_inspection']), fence: approvalRead.fence, occurredAtMs, }); } catch (error) { throw new ApprovalInspectionUnavailableError({ cause: error instanceof Error ? error : undefined, }); } return detail; }, }); }