@enactprotocol/security
Version:
Backend security library for signing enact documents
415 lines (341 loc) • 10.3 kB
Markdown
# Security Configuration Usage Guide
This document provides practical examples for using the security configuration features in the Enact Protocol security library.
## Quick Start
```typescript
import { SigningService, CryptoUtils, DEFAULT_SECURITY_CONFIG } from '@enactprotocol/security';
import type { SecurityConfig } from '@enactprotocol/security';
// Create a document to sign
const tool = {
name: "my-org/hello-world",
description: "A simple greeting tool",
command: "echo 'Hello ${name}!'",
enact: "1.0.0"
};
// Generate keys
const keyPair = CryptoUtils.generateKeyPair();
// Sign the document
const signature = SigningService.signDocument(tool, keyPair.privateKey, {
useEnactDefaults: true
});
// Verify with default config (minimumSignatures: 1, allowLocalUnsigned: true)
const isValid = SigningService.verifyDocument(tool, signature, {
useEnactDefaults: true
});
console.log('Valid:', isValid); // true
```
## Security Configuration Options
### Basic Configuration
```typescript
interface SecurityConfig {
allowLocalUnsigned?: boolean; // Allow documents without signatures
minimumSignatures?: number; // Minimum number of signatures required
}
// Default configuration
const DEFAULT_SECURITY_CONFIG: SecurityConfig = {
allowLocalUnsigned: true,
minimumSignatures: 1
};
```
## Common Usage Patterns
### 1. Permissive Mode (Default)
**Use case**: Development, local tools, single-developer workflows
```typescript
const permissiveConfig: SecurityConfig = {
allowLocalUnsigned: true,
minimumSignatures: 1
};
// This will pass even with no signatures
const documentWithoutSignatures = {
name: "local-tool",
command: "echo 'local'",
signatures: [] // Empty signatures array
};
const isValid = SigningService.verifyDocument(
documentWithoutSignatures,
{} as any, // Dummy signature
{ useEnactDefaults: true },
permissiveConfig
);
console.log('Valid:', isValid); // true
```
### 2. Single Signature Required
**Use case**: Personal tools, simple validation
```typescript
const singleSignatureConfig: SecurityConfig = {
allowLocalUnsigned: false,
minimumSignatures: 1
};
// Must have at least one valid signature
const signature = SigningService.signDocument(tool, keyPair.privateKey, {
useEnactDefaults: true
});
const isValid = SigningService.verifyDocument(
tool,
signature,
{ useEnactDefaults: true },
singleSignatureConfig
);
console.log('Valid:', isValid); // true
```
### 3. Multi-Party Approval
**Use case**: Enterprise environments, critical tools, compliance requirements
```typescript
const enterpriseConfig: SecurityConfig = {
allowLocalUnsigned: false,
minimumSignatures: 2
};
// Create multiple signatures from different parties
const developerKeys = CryptoUtils.generateKeyPair();
const reviewerKeys = CryptoUtils.generateKeyPair();
const devSignature = SigningService.signDocument(tool, developerKeys.privateKey, {
useEnactDefaults: true
});
const reviewSignature = SigningService.signDocument(tool, reviewerKeys.privateKey, {
useEnactDefaults: true
});
// Add signatures to document
const toolWithMultipleSignatures = {
...tool,
signatures: [devSignature, reviewSignature]
};
const isValid = SigningService.verifyDocument(
toolWithMultipleSignatures,
devSignature, // This gets ignored since document has signatures array
{ useEnactDefaults: true },
enterpriseConfig
);
console.log('Valid:', isValid); // true
```
### 4. Strict Security Mode
**Use case**: Production environments, sensitive operations
```typescript
const strictConfig: SecurityConfig = {
allowLocalUnsigned: false,
minimumSignatures: 3
};
// Requires 3 signatures: developer + reviewer + security team
const devSignature = SigningService.signDocument(tool, developerKeys.privateKey, {
useEnactDefaults: true
});
const reviewSignature = SigningService.signDocument(tool, reviewerKeys.privateKey, {
useEnactDefaults: true
});
const securityKeys = CryptoUtils.generateKeyPair();
const secSignature = SigningService.signDocument(tool, securityKeys.privateKey, {
useEnactDefaults: true
});
const secureDocument = {
...tool,
signatures: [devSignature, reviewSignature, secSignature]
};
const isValid = SigningService.verifyDocument(
secureDocument,
devSignature,
{ useEnactDefaults: true },
strictConfig
);
console.log('Valid:', isValid); // true
```
## Configuration Scenarios
### Development Environment
```typescript
const devConfig: SecurityConfig = {
allowLocalUnsigned: true,
minimumSignatures: 1
};
// ✅ Allows unsigned local tools
// ✅ Single signature sufficient for signed tools
```
### Testing Environment
```typescript
const testConfig: SecurityConfig = {
allowLocalUnsigned: false,
minimumSignatures: 1
};
// ❌ Requires all tools to be signed
// ✅ Single signature sufficient
```
### Production Environment
```typescript
const prodConfig: SecurityConfig = {
allowLocalUnsigned: false,
minimumSignatures: 2
};
// ❌ No unsigned tools allowed
// ❌ Requires multiple signatures for approval
```
## Error Handling
### Insufficient Signatures
```typescript
const config: SecurityConfig = {
allowLocalUnsigned: false,
minimumSignatures: 2
};
const toolWithOneSignature = {
...tool,
signatures: [signature] // Only 1 signature, need 2
};
const isValid = SigningService.verifyDocument(
toolWithOneSignature,
signature,
{ useEnactDefaults: true },
config
);
console.log('Valid:', isValid); // false - insufficient signatures
```
### Invalid Signatures
```typescript
const invalidSignature = {
signature: 'invalid_signature_data',
publicKey: 'invalid_key',
algorithm: 'secp256k1',
created: Date.now()
};
const toolWithInvalidSignature = {
...tool,
signatures: [invalidSignature]
};
const isValid = SigningService.verifyDocument(
toolWithInvalidSignature,
invalidSignature,
{ useEnactDefaults: true }
);
console.log('Valid:', isValid); // false - cryptographically invalid
```
## Best Practices
### 1. Environment-Based Configuration
```typescript
const getSecurityConfig = (environment: string): SecurityConfig => {
switch (environment) {
case 'development':
return {
allowLocalUnsigned: true,
minimumSignatures: 1
};
case 'staging':
return {
allowLocalUnsigned: false,
minimumSignatures: 1
};
case 'production':
return {
allowLocalUnsigned: false,
minimumSignatures: 2
};
default:
return DEFAULT_SECURITY_CONFIG;
}
};
const config = getSecurityConfig(process.env.NODE_ENV || 'development');
```
### 2. Gradual Security Increase
```typescript
// Start permissive, increase security over time
const phases = {
phase1: { allowLocalUnsigned: true, minimumSignatures: 1 }, // Launch
phase2: { allowLocalUnsigned: false, minimumSignatures: 1 }, // Require signing
phase3: { allowLocalUnsigned: false, minimumSignatures: 2 } // Require approval
};
```
### 3. Role-Based Workflows
```typescript
// Different signature requirements for different types of tools
const getConfigForTool = (toolType: string): SecurityConfig => {
switch (toolType) {
case 'utility':
return { allowLocalUnsigned: true, minimumSignatures: 1 };
case 'deployment':
return { allowLocalUnsigned: false, minimumSignatures: 2 };
case 'security':
return { allowLocalUnsigned: false, minimumSignatures: 3 };
default:
return DEFAULT_SECURITY_CONFIG;
}
};
```
## Integration Examples
### CLI Tool Integration
```typescript
import { SigningService } from '@enactprotocol/security';
class ToolVerifier {
constructor(private securityConfig: SecurityConfig) {}
async verifyTool(tool: EnactDocument): Promise<boolean> {
// Tool must have at least one signature to verify
if (!tool.signatures?.length && !this.securityConfig.allowLocalUnsigned) {
return false;
}
// If no signatures and local unsigned allowed
if (!tool.signatures?.length && this.securityConfig.allowLocalUnsigned) {
return true;
}
// Verify signatures using first signature as reference
return SigningService.verifyDocument(
tool,
tool.signatures[0],
{ useEnactDefaults: true },
this.securityConfig
);
}
}
// Usage
const verifier = new ToolVerifier({
allowLocalUnsigned: false,
minimumSignatures: 2
});
const isToolValid = await verifier.verifyTool(someTool);
```
### Config File Integration
```typescript
// ~/.enact/security/config.json
{
"minimumSignatures": 1,
"allowLocalUnsigned": true
}
// Load configuration
import { readFileSync } from 'fs';
import { join } from 'path';
import { homedir } from 'os';
const loadSecurityConfig = (): SecurityConfig => {
try {
const configPath = join(homedir(), '.enact', 'security', 'config.json');
const config = JSON.parse(readFileSync(configPath, 'utf8'));
return {
allowLocalUnsigned: config.allowLocalUnsigned ?? true,
minimumSignatures: config.minimumSignatures ?? 1
};
} catch {
return DEFAULT_SECURITY_CONFIG;
}
};
const config = loadSecurityConfig();
```
## Migration Guide
### From No Security Config
```typescript
// Before
const isValid = SigningService.verifyDocument(tool, signature, options);
// After
const isValid = SigningService.verifyDocument(
tool,
signature,
options,
DEFAULT_SECURITY_CONFIG // Uses minimumSignatures: 1, allowLocalUnsigned: true
);
```
### Upgrading Security Gradually
```typescript
// Week 1: Start with permissive defaults
let config = DEFAULT_SECURITY_CONFIG;
// Week 2: Require signing for new tools
config = { ...config, allowLocalUnsigned: false };
// Week 4: Require multiple signatures for critical tools
if (tool.annotations?.critical) {
config = { ...config, minimumSignatures: 2 };
}
```
## Summary
The security configuration system provides flexible verification policies that can adapt to different environments and security requirements:
- **Development**: Permissive, allows unsigned tools
- **Testing**: Requires signatures but single signature sufficient
- **Production**: Strict multi-party approval required
Use the configuration to gradually increase security maturity while maintaining usability for developers.