/** * Runtime enforcement of metadata contracts * Ensures augmentations only access declared fields */ import { BrainyAugmentation, MetadataAccess } from './brainyAugmentation.js' export class MetadataEnforcer { /** * Enforce metadata access based on augmentation contract * Returns a wrapped metadata object that enforces the contract */ static enforce( augmentation: BrainyAugmentation, metadata: any, operation: 'read' | 'write' = 'write' ): any { // Handle simple contracts if (augmentation.metadata === 'none') { // No access at all if (operation === 'read') return null throw new Error(`Augmentation '${augmentation.name}' has metadata='none' - cannot access metadata`) } if (augmentation.metadata === 'readonly') { if (operation === 'read') { // Return frozen deep clone for read-only access return deepFreeze(deepClone(metadata)) } throw new Error(`Augmentation '${augmentation.name}' has metadata='readonly' - cannot write`) } // Handle specific field access const access = augmentation.metadata as MetadataAccess if (operation === 'read') { // For reads, filter to allowed fields if (access.reads === '*') { return deepClone(metadata) // Can read everything } if (!access.reads) { return {} // No read access } // Filter to specific fields const filtered: any = {} for (const field of access.reads) { if (field.includes('.')) { // Handle nested fields like '_brainy.deleted' const parts = field.split('.') let source = metadata let target = filtered for (let i = 0; i < parts.length - 1; i++) { const part = parts[i] if (!source[part]) break if (!target[part]) target[part] = {} source = source[part] target = target[part] } const lastPart = parts[parts.length - 1] if (source && lastPart in source) { target[lastPart] = source[lastPart] } } else { // Simple field if (field in metadata) { filtered[field] = metadata[field] } } } return filtered } // For writes, create a proxy that validates return new Proxy(metadata, { set(target, prop, value) { const field = String(prop) // Check if write is allowed if (access.writes === '*') { // Can write anything target[prop] = value return true } if (!access.writes || !access.writes.includes(field)) { throw new Error( `Augmentation '${augmentation.name}' cannot write to field '${field}'. ` + `Allowed writes: ${access.writes?.join(', ') || 'none'}` ) } // Check namespace if specified if (access.namespace && !field.startsWith(access.namespace)) { console.warn( `Augmentation '${augmentation.name}' writing outside its namespace. ` + `Expected: ${access.namespace}.*, got: ${field}` ) } target[prop] = value return true }, deleteProperty(target, prop) { const field = String(prop) // Deletion counts as a write if (access.writes === '*' || access.writes?.includes(field)) { delete target[prop] return true } throw new Error( `Augmentation '${augmentation.name}' cannot delete field '${field}'` ) } }) } /** * Validate that an augmentation's actual behavior matches its contract * Used in testing to verify contracts are accurate */ static async validateContract( augmentation: BrainyAugmentation, testMetadata: any = { test: 'data', _brainy: { deleted: false } } ): Promise<{ valid: boolean; violations: string[] }> { const violations: string[] = [] // Test read access try { const readable = this.enforce(augmentation, testMetadata, 'read') if (augmentation.metadata === 'none' && readable !== null) { violations.push(`Contract says 'none' but got readable metadata`) } } catch (error) { violations.push(`Read enforcement error: ${error}`) } // Test write access try { const writable = this.enforce(augmentation, testMetadata, 'write') if (augmentation.metadata === 'none') { violations.push(`Contract says 'none' but got writable metadata`) } if (augmentation.metadata === 'readonly') { // Try to write - should fail try { writable.testWrite = 'value' violations.push(`Contract says 'readonly' but write succeeded`) } catch { // Expected to fail } } } catch (error) { // Expected for 'none' and 'readonly' on write if (augmentation.metadata !== 'none' && augmentation.metadata !== 'readonly') { violations.push(`Write enforcement error: ${error}`) } } return { valid: violations.length === 0, violations } } } // Helper functions function deepClone(obj: any): any { if (obj === null || typeof obj !== 'object') return obj if (obj instanceof Date) return new Date(obj) if (obj instanceof Array) return obj.map(item => deepClone(item)) const cloned: any = {} for (const key in obj) { if (obj.hasOwnProperty(key)) { cloned[key] = deepClone(obj[key]) } } return cloned } function deepFreeze(obj: any): any { Object.freeze(obj) Object.getOwnPropertyNames(obj).forEach(prop => { if (obj[prop] !== null && (typeof obj[prop] === 'object' || typeof obj[prop] === 'function') && !Object.isFrozen(obj[prop])) { deepFreeze(obj[prop]) } }) return obj }