brainy/.recovery-workspace/dist-backup-20250910-141917/augmentations/metadataEnforcer.js

171 lines
6.5 KiB
JavaScript
Raw Normal View History

/**
* Runtime enforcement of metadata contracts
* Ensures augmentations only access declared fields
*/
export class MetadataEnforcer {
/**
* Enforce metadata access based on augmentation contract
* Returns a wrapped metadata object that enforces the contract
*/
static enforce(augmentation, metadata, operation = 'write') {
// 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;
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 = {};
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, testMetadata = { test: 'data', _brainy: { deleted: false } }) {
const violations = [];
// 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) {
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 = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
cloned[key] = deepClone(obj[key]);
}
}
return cloned;
}
function deepFreeze(obj) {
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;
}
//# sourceMappingURL=metadataEnforcer.js.map