tasq/node_modules/@claude-flow/guidance/dist/authority.js

558 lines
18 KiB
JavaScript

/**
* Human Authority Gate + Irreversibility Classification
*
* Provides typed boundaries between agent, human, and institutional authority,
* along with irreversibility classification for actions that require elevated
* proof and pre-commit simulation.
*
* AuthorityGate:
* - Defines authority levels (agent, human, institutional, regulatory)
* - Maintains a registry of authority scopes and permissions
* - Checks if a given authority level can perform an action
* - Determines if escalation is required
* - Records signed human interventions for audit trails
*
* IrreversibilityClassifier:
* - Classifies actions as reversible, costly-reversible, or irreversible
* - Uses configurable pattern matching (regex arrays)
* - Determines required proof levels (standard, elevated, maximum)
* - Identifies actions requiring pre-commit simulation
*
* Human interventions are cryptographically signed using HMAC-SHA256 to
* create an immutable audit trail of override decisions.
*
* @module @claude-flow/guidance/authority
*/
import { createHmac, randomUUID } from 'node:crypto';
import { timingSafeEqual } from './crypto-utils.js';
// ============================================================================
// Default Configurations
// ============================================================================
/**
* Default authority scopes for each level.
*/
const DEFAULT_AUTHORITY_SCOPES = [
{
level: 'agent',
permissions: [
'read_file',
'analyze_code',
'suggest_changes',
'run_tests',
'generate_documentation',
],
overrideScope: [],
escalationRequired: false,
},
{
level: 'human',
permissions: [
'write_file',
'modify_code',
'deploy_staging',
'create_branch',
'merge_pr',
'delete_resource',
],
overrideScope: ['read_file', 'analyze_code', 'suggest_changes', 'run_tests'],
escalationRequired: false,
},
{
level: 'institutional',
permissions: [
'deploy_production',
'modify_security_policy',
'grant_access',
'revoke_access',
'approve_budget',
'sign_contract',
],
overrideScope: [
'write_file',
'modify_code',
'deploy_staging',
'create_branch',
],
escalationRequired: false,
},
{
level: 'regulatory',
permissions: [
'approve_compliance',
'certify_audit',
'approve_data_transfer',
'approve_privacy_policy',
'issue_license',
],
overrideScope: [
'deploy_production',
'modify_security_policy',
'grant_access',
'approve_budget',
],
escalationRequired: false,
},
];
/**
* Default patterns for irreversible actions.
*/
const DEFAULT_IRREVERSIBLE_PATTERNS = [
'send.*email',
'publish.*package',
'process.*payment',
'execute.*payment',
'delete.*permanent',
'drop.*database',
'revoke.*certificate',
'propagate.*dns',
'broadcast.*message',
'sign.*transaction',
'commit.*blockchain',
'release.*funds',
];
/**
* Default patterns for costly-reversible actions.
*/
const DEFAULT_COSTLY_REVERSIBLE_PATTERNS = [
'migrate.*database',
'deploy.*production',
'rollback.*deployment',
'update.*config',
'modify.*schema',
'send.*notification',
'create.*user',
'delete.*user',
'grant.*permission',
'revoke.*permission',
'scale.*infrastructure',
'provision.*resource',
];
/**
* Default patterns for reversible actions.
*/
const DEFAULT_REVERSIBLE_PATTERNS = [
'read.*file',
'analyze.*code',
'generate.*report',
'run.*test',
'preview.*change',
'simulate.*deployment',
'validate.*input',
'check.*status',
];
// ============================================================================
// Authority Hierarchy
// ============================================================================
/**
* Ordered authority hierarchy from lowest to highest.
*/
const AUTHORITY_HIERARCHY = [
'agent',
'human',
'institutional',
'regulatory',
];
// ============================================================================
// AuthorityGate
// ============================================================================
/**
* Gate that enforces authority boundaries and records human interventions.
*
* Maintains a registry of authority scopes, checks permissions, determines
* escalation requirements, and creates cryptographically signed intervention
* records for audit trails.
*/
export class AuthorityGate {
scopes = new Map();
interventions = [];
signatureSecret;
constructor(config = {}) {
// Initialize scopes
const scopesToRegister = config.scopes ?? DEFAULT_AUTHORITY_SCOPES;
for (const scope of scopesToRegister) {
this.scopes.set(scope.level, scope);
}
// Initialize signature secret
this.signatureSecret =
config.signatureSecret ?? randomUUID() + randomUUID();
}
/**
* Check if a given authority level can perform an action.
*
* Returns a result indicating whether the action is allowed, the required
* authority level, and a human-readable explanation.
*/
canPerform(level, action) {
const scope = this.scopes.get(level);
if (!scope) {
return {
allowed: false,
requiredLevel: 'regulatory',
currentLevel: level,
reason: `Unknown authority level: ${level}`,
};
}
// Check if action is in this level's permissions
if (this.hasPermission(scope, action)) {
return {
allowed: true,
requiredLevel: level,
currentLevel: level,
reason: `Action '${action}' is permitted at ${level} authority level`,
};
}
// Find minimum required authority level
const requiredLevel = this.getMinimumAuthority(action);
return {
allowed: false,
requiredLevel,
currentLevel: level,
reason: `Action '${action}' requires ${requiredLevel} authority level (current: ${level})`,
};
}
/**
* Check if an action requires escalation from the current authority level.
*/
requiresEscalation(level, action) {
const checkResult = this.canPerform(level, action);
if (checkResult.allowed) {
return false;
}
// Escalation is required if a higher authority level is needed
const currentIndex = AUTHORITY_HIERARCHY.indexOf(level);
const requiredIndex = AUTHORITY_HIERARCHY.indexOf(checkResult.requiredLevel);
return requiredIndex > currentIndex;
}
/**
* Get the minimum authority level required to perform an action.
*
* Returns the lowest authority level that has permission for this action.
* If no level has permission, returns 'regulatory' as the highest level.
*/
getMinimumAuthority(action) {
// Check levels from lowest to highest
for (const level of AUTHORITY_HIERARCHY) {
const scope = this.scopes.get(level);
if (scope && this.hasPermission(scope, action)) {
return level;
}
}
// If no level has permission, require highest authority
return 'regulatory';
}
/**
* Record a human intervention with cryptographic signature.
*
* Creates an immutable audit record of the intervention decision.
* The signature is computed using HMAC-SHA256 over the intervention details.
*/
recordIntervention(intervention) {
const id = randomUUID();
const signature = this.signIntervention({
id,
...intervention,
signature: '', // Placeholder for signature computation
});
const signedIntervention = {
id,
...intervention,
signature,
};
this.interventions.push(signedIntervention);
return signedIntervention;
}
/**
* Get all recorded interventions.
*/
getInterventions() {
return [...this.interventions];
}
/**
* Get interventions for a specific action.
*/
getInterventionsForAction(action) {
return this.interventions.filter(i => i.action === action);
}
/**
* Get interventions by authority level.
*/
getInterventionsByLevel(level) {
return this.interventions.filter(i => i.authorityLevel === level);
}
/**
* Verify the signature of an intervention.
*/
verifyIntervention(intervention) {
const expectedSignature = this.signIntervention(intervention);
return timingSafeEqual(expectedSignature, intervention.signature);
}
/**
* Get the number of recorded interventions.
*/
get interventionCount() {
return this.interventions.length;
}
/**
* Get all registered authority levels.
*/
getAuthorityLevels() {
return [...this.scopes.keys()];
}
/**
* Get the scope for a specific authority level.
*/
getScope(level) {
return this.scopes.get(level);
}
/**
* Add or update an authority scope.
*/
registerScope(scope) {
this.scopes.set(scope.level, scope);
}
// ===== Private =====
/**
* Check if a scope has permission for an action.
*
* Uses exact match and pattern matching (with wildcards).
*/
hasPermission(scope, action) {
// Check exact match
if (scope.permissions.includes(action)) {
return true;
}
// Check pattern match (treat * as wildcard)
for (const permission of scope.permissions) {
if (this.matchesPattern(action, permission)) {
return true;
}
}
// Check override scope
if (scope.overrideScope.includes(action)) {
return true;
}
for (const override of scope.overrideScope) {
if (this.matchesPattern(action, override)) {
return true;
}
}
return false;
}
/**
* Check if an action matches a permission pattern.
*
* Supports simple wildcard patterns (e.g., "deploy_*").
*/
matchesPattern(action, pattern) {
if (!pattern.includes('*')) {
return action === pattern;
}
// Convert wildcard pattern to regex
const regexPattern = pattern
.replace(/[.+?^${}()|[\]\\]/g, '\\$&') // Escape regex special chars
.replace(/\*/g, '.*'); // Replace * with .*
const regex = new RegExp(`^${regexPattern}$`);
return regex.test(action);
}
/**
* Sign an intervention using HMAC-SHA256.
*/
signIntervention(intervention) {
const payload = JSON.stringify({
id: intervention.id,
timestamp: intervention.timestamp,
authorityLevel: intervention.authorityLevel,
action: intervention.action,
reason: intervention.reason,
signedBy: intervention.signedBy,
metadata: intervention.metadata,
});
const hmac = createHmac('sha256', this.signatureSecret);
hmac.update(payload);
return hmac.digest('hex');
}
}
// ============================================================================
// IrreversibilityClassifier
// ============================================================================
/**
* Classifies actions by their reversibility to determine required proof levels
* and whether pre-commit simulation is needed.
*
* Uses configurable regex patterns to identify irreversible, costly-reversible,
* and reversible actions. Irreversible actions require maximum proof and
* pre-commit simulation.
*/
export class IrreversibilityClassifier {
irreversiblePatterns;
costlyReversiblePatterns;
reversiblePatterns;
constructor(config = {}) {
this.irreversiblePatterns = (config.irreversiblePatterns ?? DEFAULT_IRREVERSIBLE_PATTERNS).map(p => new RegExp(p, 'i'));
this.costlyReversiblePatterns = (config.costlyReversiblePatterns ?? DEFAULT_COSTLY_REVERSIBLE_PATTERNS).map(p => new RegExp(p, 'i'));
this.reversiblePatterns = (config.reversiblePatterns ?? DEFAULT_REVERSIBLE_PATTERNS).map(p => new RegExp(p, 'i'));
}
/**
* Classify an action by its reversibility.
*
* Checks patterns in order: irreversible → costly-reversible → reversible.
* If no patterns match, defaults to 'costly-reversible' as a safe default.
*/
classify(action) {
// Check irreversible patterns first (highest risk)
const irreversibleMatches = this.findMatches(action, this.irreversiblePatterns);
if (irreversibleMatches.length > 0) {
return {
classification: 'irreversible',
matchedPatterns: irreversibleMatches,
requiredProofLevel: 'maximum',
requiresSimulation: true,
};
}
// Check costly-reversible patterns
const costlyMatches = this.findMatches(action, this.costlyReversiblePatterns);
if (costlyMatches.length > 0) {
return {
classification: 'costly-reversible',
matchedPatterns: costlyMatches,
requiredProofLevel: 'elevated',
requiresSimulation: true,
};
}
// Check reversible patterns
const reversibleMatches = this.findMatches(action, this.reversiblePatterns);
if (reversibleMatches.length > 0) {
return {
classification: 'reversible',
matchedPatterns: reversibleMatches,
requiredProofLevel: 'standard',
requiresSimulation: false,
};
}
// Default to costly-reversible if no patterns match (safe default)
return {
classification: 'costly-reversible',
matchedPatterns: [],
requiredProofLevel: 'elevated',
requiresSimulation: true,
};
}
/**
* Get the required proof level for an action.
*
* - 'maximum' for irreversible actions
* - 'elevated' for costly-reversible actions
* - 'standard' for reversible actions
*/
getRequiredProofLevel(action) {
return this.classify(action).requiredProofLevel;
}
/**
* Check if an action requires pre-commit simulation.
*
* Returns true for irreversible and costly-reversible actions.
*/
requiresPreCommitSimulation(action) {
return this.classify(action).requiresSimulation;
}
/**
* Get all configured patterns for a classification.
*/
getPatterns(classification) {
switch (classification) {
case 'irreversible':
return this.irreversiblePatterns.map(p => p.source);
case 'costly-reversible':
return this.costlyReversiblePatterns.map(p => p.source);
case 'reversible':
return this.reversiblePatterns.map(p => p.source);
}
}
/**
* Add a pattern to a classification.
*
* Validates the pattern against ReDoS heuristics before accepting it.
* Rejects patterns with nested quantifiers (e.g., `(a+)+`) that can
* cause catastrophic backtracking.
*
* @throws Error if the pattern is invalid regex or contains ReDoS-prone constructs
*/
addPattern(classification, pattern) {
// ReDoS heuristic: reject nested quantifiers like (a+)+, (a*)+, (a+)*, etc.
if (/([+*]|\{[0-9]+,?\})\s*\)[\s]*[+*]|\{[0-9]+,?\}/.test(pattern)) {
throw new Error(`Pattern rejected: nested quantifiers detected (potential ReDoS): ${pattern}`);
}
// Also reject patterns longer than 500 chars as a sanity bound
if (pattern.length > 500) {
throw new Error(`Pattern rejected: exceeds maximum length of 500 characters`);
}
const regex = new RegExp(pattern, 'i');
switch (classification) {
case 'irreversible':
this.irreversiblePatterns.push(regex);
break;
case 'costly-reversible':
this.costlyReversiblePatterns.push(regex);
break;
case 'reversible':
this.reversiblePatterns.push(regex);
break;
}
}
// ===== Private =====
/**
* Find all patterns that match an action.
*/
findMatches(action, patterns) {
const matches = [];
for (const pattern of patterns) {
if (pattern.test(action)) {
matches.push(pattern.source);
}
}
return matches;
}
}
// ============================================================================
// Factory Functions
// ============================================================================
/**
* Create an AuthorityGate with optional configuration.
*/
export function createAuthorityGate(config) {
return new AuthorityGate(config);
}
/**
* Create an IrreversibilityClassifier with optional configuration.
*/
export function createIrreversibilityClassifier(config) {
return new IrreversibilityClassifier(config);
}
// ============================================================================
// Helpers
// ============================================================================
/**
* Check if one authority level is higher than another.
*/
export function isHigherAuthority(level1, level2) {
const index1 = AUTHORITY_HIERARCHY.indexOf(level1);
const index2 = AUTHORITY_HIERARCHY.indexOf(level2);
return index1 > index2;
}
/**
* Get the next higher authority level, if any.
*/
export function getNextHigherAuthority(level) {
const index = AUTHORITY_HIERARCHY.indexOf(level);
if (index === -1 || index === AUTHORITY_HIERARCHY.length - 1) {
return null;
}
return AUTHORITY_HIERARCHY[index + 1];
}
/**
* Get the authority hierarchy as an ordered array.
*/
export function getAuthorityHierarchy() {
return [...AUTHORITY_HIERARCHY];
}
//# sourceMappingURL=authority.js.map