This comprehensive refactoring addresses critical architectural issues: IMPORT CYCLE RESOLUTION: • pkg/crypto ↔ pkg/slurp/roles: Created pkg/security/access_levels.go • pkg/ucxl → pkg/dht: Created pkg/storage/interfaces.go • pkg/slurp/leader → pkg/election → pkg/slurp/storage: Moved types to pkg/election/interfaces.go MODULE PATH MIGRATION: • Changed from github.com/anthonyrawlins/bzzz to chorus.services/bzzz • Updated all import statements across 115+ files • Maintains compatibility while removing personal GitHub account dependency TYPE SYSTEM IMPROVEMENTS: • Resolved duplicate type declarations in crypto package • Added missing type definitions (RoleStatus, TimeRestrictions, KeyStatus, KeyRotationResult) • Proper interface segregation to prevent future cycles ARCHITECTURAL BENEFITS: • Build now progresses past structural issues to normal dependency resolution • Cleaner separation of concerns between packages • Eliminates circular dependencies that prevented compilation • Establishes foundation for scalable codebase growth 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
959 lines
31 KiB
Go
959 lines
31 KiB
Go
// Package crypto_test provides comprehensive tests for role-based encryption.
|
|
//
|
|
// This test suite validates the enterprise-grade security features including:
|
|
// - Multi-layer encryption and decryption operations
|
|
// - Role-based access control and permission enforcement
|
|
// - Key management and rotation procedures
|
|
// - Audit logging and compliance monitoring
|
|
// - Performance and security benchmarks
|
|
// - Edge cases and error handling
|
|
//
|
|
// Test Categories:
|
|
// - Unit tests for individual components
|
|
// - Integration tests for end-to-end workflows
|
|
// - Security tests for vulnerability assessment
|
|
// - Performance tests for scalability validation
|
|
// - Compliance tests for regulatory requirements
|
|
|
|
package crypto
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/stretchr/testify/suite"
|
|
|
|
"chorus.services/bzzz/pkg/config"
|
|
"chorus.services/bzzz/pkg/ucxl"
|
|
slurpContext "chorus.services/bzzz/pkg/slurp/context"
|
|
)
|
|
|
|
// RoleCryptoTestSuite provides comprehensive testing for role-based encryption
|
|
type RoleCryptoTestSuite struct {
|
|
suite.Suite
|
|
config *config.Config
|
|
ageCrypto *AgeCrypto
|
|
auditLogger *MockAuditLogger
|
|
roleCrypto *RoleCrypto
|
|
keyManager *KeyManager
|
|
keyStore *MockKeyStore
|
|
accessControl *AccessControlMatrix
|
|
}
|
|
|
|
// MockAuditLogger implements AuditLogger for testing
|
|
type MockAuditLogger struct {
|
|
accessLogs []*AccessLogEntry
|
|
keyRotations []*KeyRotationEvent
|
|
securityEvents []*SecurityEvent
|
|
}
|
|
|
|
func (m *MockAuditLogger) LogAccess(entry *AccessLogEntry) error {
|
|
m.accessLogs = append(m.accessLogs, entry)
|
|
return nil
|
|
}
|
|
|
|
func (m *MockAuditLogger) LogKeyRotation(event *KeyRotationEvent) error {
|
|
m.keyRotations = append(m.keyRotations, event)
|
|
return nil
|
|
}
|
|
|
|
func (m *MockAuditLogger) LogSecurityEvent(event *SecurityEvent) error {
|
|
m.securityEvents = append(m.securityEvents, event)
|
|
return nil
|
|
}
|
|
|
|
func (m *MockAuditLogger) GetAuditTrail(criteria *AuditCriteria) ([]*AuditEvent, error) {
|
|
events := []*AuditEvent{}
|
|
for _, access := range m.accessLogs {
|
|
events = append(events, &AuditEvent{
|
|
EventID: fmt.Sprintf("access_%s", access.UserID),
|
|
EventType: "access",
|
|
Timestamp: access.AccessTime,
|
|
UserID: access.UserID,
|
|
Data: map[string]interface{}{
|
|
"access_type": access.AccessType,
|
|
"success": access.Success,
|
|
},
|
|
})
|
|
}
|
|
return events, nil
|
|
}
|
|
|
|
// MockKeyStore implements KeyStore for testing
|
|
type MockKeyStore struct {
|
|
keys map[string]*SecureKeyData
|
|
}
|
|
|
|
func NewMockKeyStore() *MockKeyStore {
|
|
return &MockKeyStore{
|
|
keys: make(map[string]*SecureKeyData),
|
|
}
|
|
}
|
|
|
|
func (m *MockKeyStore) StoreKey(keyID string, keyData *SecureKeyData) error {
|
|
m.keys[keyID] = keyData
|
|
return nil
|
|
}
|
|
|
|
func (m *MockKeyStore) RetrieveKey(keyID string) (*SecureKeyData, error) {
|
|
if key, exists := m.keys[keyID]; exists {
|
|
return key, nil
|
|
}
|
|
return nil, fmt.Errorf("key not found: %s", keyID)
|
|
}
|
|
|
|
func (m *MockKeyStore) DeleteKey(keyID string) error {
|
|
delete(m.keys, keyID)
|
|
return nil
|
|
}
|
|
|
|
func (m *MockKeyStore) ListKeys(filter *KeyFilter) ([]*KeyMetadata, error) {
|
|
metadata := []*KeyMetadata{}
|
|
for keyID, keyData := range m.keys {
|
|
if filter != nil && filter.RoleID != "" {
|
|
if roleID, ok := keyData.Metadata["role_id"].(string); !ok || roleID != filter.RoleID {
|
|
continue
|
|
}
|
|
}
|
|
|
|
meta := &KeyMetadata{
|
|
KeyID: keyID,
|
|
KeyType: keyData.KeyType,
|
|
CreatedAt: keyData.CreatedAt,
|
|
Status: keyData.Status,
|
|
SecurityLevel: AccessMedium, // Default for tests
|
|
}
|
|
|
|
if roleID, ok := keyData.Metadata["role_id"].(string); ok {
|
|
meta.RoleID = roleID
|
|
}
|
|
|
|
metadata = append(metadata, meta)
|
|
}
|
|
return metadata, nil
|
|
}
|
|
|
|
func (m *MockKeyStore) BackupKeys(criteria *BackupCriteria) (*KeyBackup, error) {
|
|
return &KeyBackup{
|
|
BackupID: fmt.Sprintf("backup_%d", time.Now().Unix()),
|
|
CreatedAt: time.Now(),
|
|
KeyCount: len(m.keys),
|
|
Checksum: "mock_checksum",
|
|
}, nil
|
|
}
|
|
|
|
func (m *MockKeyStore) RestoreKeys(backup *KeyBackup) error {
|
|
return nil
|
|
}
|
|
|
|
// MockPolicyEngine implements PolicyEngine for testing
|
|
type MockPolicyEngine struct {
|
|
policies map[string]*AccessPolicy
|
|
}
|
|
|
|
func NewMockPolicyEngine() *MockPolicyEngine {
|
|
return &MockPolicyEngine{
|
|
policies: make(map[string]*AccessPolicy),
|
|
}
|
|
}
|
|
|
|
func (m *MockPolicyEngine) EvaluatePolicy(ctx context.Context, request *AccessRequest) (*PolicyDecision, error) {
|
|
// Simple mock evaluation - permit by default for testing
|
|
decision := &PolicyDecision{
|
|
RequestID: request.RequestID,
|
|
Decision: DecisionPermit,
|
|
Reason: "Mock policy evaluation - permit",
|
|
MatchedPolicies: []string{"mock_policy"},
|
|
AppliedRules: []string{"mock_rule"},
|
|
ConfidenceScore: 0.95,
|
|
RiskScore: 0.2,
|
|
EvaluationTime: 10 * time.Millisecond,
|
|
EvaluatedAt: time.Now(),
|
|
}
|
|
|
|
// Deny access for test cases that need denial
|
|
if strings.Contains(request.UserID, "unauthorized") {
|
|
decision.Decision = DecisionDeny
|
|
decision.Reason = "Unauthorized user"
|
|
decision.RiskScore = 0.9
|
|
}
|
|
|
|
return decision, nil
|
|
}
|
|
|
|
func (m *MockPolicyEngine) CompilePolicy(policy *AccessPolicy) (*CompiledPolicy, error) {
|
|
return &CompiledPolicy{
|
|
PolicyID: policy.PolicyID,
|
|
CompiledAt: time.Now(),
|
|
CompilerVersion: "mock_v1.0",
|
|
}, nil
|
|
}
|
|
|
|
func (m *MockPolicyEngine) ValidatePolicy(policy *AccessPolicy) (*PolicyValidationResult, error) {
|
|
return &PolicyValidationResult{
|
|
Valid: true,
|
|
Errors: []string{},
|
|
Warnings: []string{},
|
|
ValidatedAt: time.Now(),
|
|
ValidationTime: 5 * time.Millisecond,
|
|
}, nil
|
|
}
|
|
|
|
func (m *MockPolicyEngine) LoadPolicies(policies []*AccessPolicy) error {
|
|
for _, policy := range policies {
|
|
m.policies[policy.PolicyID] = policy
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m *MockPolicyEngine) ReloadPolicies() error {
|
|
return nil
|
|
}
|
|
|
|
// MockAttributeProvider implements AttributeProvider for testing
|
|
type MockAttributeProvider struct{}
|
|
|
|
func (m *MockAttributeProvider) GetUserAttributes(userID string) (*UserAttributes, error) {
|
|
return &UserAttributes{
|
|
UserID: userID,
|
|
Department: "Engineering",
|
|
Title: "Software Engineer",
|
|
ClearanceLevel: "medium",
|
|
EmploymentType: "full_time",
|
|
StartDate: time.Now().AddDate(-1, 0, 0),
|
|
Location: "headquarters",
|
|
}, nil
|
|
}
|
|
|
|
func (m *MockAttributeProvider) GetResourceAttributes(resource string) (*ResourceAttributes, error) {
|
|
return &ResourceAttributes{
|
|
ResourceID: resource,
|
|
Classification: "internal",
|
|
Sensitivity: "medium",
|
|
DataType: "context",
|
|
CreatedAt: time.Now().AddDate(0, -1, 0),
|
|
UpdatedAt: time.Now(),
|
|
}, nil
|
|
}
|
|
|
|
func (m *MockAttributeProvider) GetEnvironmentAttributes() (*EnvironmentAttributes, error) {
|
|
return &EnvironmentAttributes{
|
|
CurrentTime: time.Now(),
|
|
BusinessHours: true,
|
|
NetworkZone: "internal",
|
|
DeviceType: "workstation",
|
|
DeviceTrust: "trusted",
|
|
ConnectionType: "wired",
|
|
ThreatLevel: "low",
|
|
ComplianceMode: "standard",
|
|
MaintenanceMode: false,
|
|
}, nil
|
|
}
|
|
|
|
func (m *MockAttributeProvider) GetContextAttributes(ctx context.Context) (*ContextAttributes, error) {
|
|
return &ContextAttributes{
|
|
RequestType: "api",
|
|
ApplicationContext: "slurp_system",
|
|
BusinessContext: "development",
|
|
TechnicalContext: "microservice",
|
|
ComplianceContext: "internal",
|
|
RiskContext: "low",
|
|
}, nil
|
|
}
|
|
|
|
// SetupSuite initializes the test suite
|
|
func (suite *RoleCryptoTestSuite) SetupSuite() {
|
|
// Create test configuration
|
|
suite.config = &config.Config{
|
|
Agent: config.Agent{
|
|
ID: "test_agent",
|
|
Role: "backend_developer",
|
|
},
|
|
}
|
|
|
|
// Initialize components
|
|
suite.auditLogger = &MockAuditLogger{
|
|
accessLogs: []*AccessLogEntry{},
|
|
keyRotations: []*KeyRotationEvent{},
|
|
securityEvents: []*SecurityEvent{},
|
|
}
|
|
|
|
suite.ageCrypto = NewAgeCrypto(suite.config)
|
|
|
|
suite.keyStore = NewMockKeyStore()
|
|
|
|
var err error
|
|
suite.keyManager, err = NewKeyManager(suite.config, suite.keyStore, suite.auditLogger)
|
|
suite.Require().NoError(err)
|
|
|
|
adminKeyManager := NewAdminKeyManager(suite.config, "test_node")
|
|
|
|
suite.roleCrypto, err = NewRoleCrypto(suite.config, suite.ageCrypto, adminKeyManager, suite.auditLogger)
|
|
suite.Require().NoError(err)
|
|
|
|
// Initialize access control
|
|
policyEngine := NewMockPolicyEngine()
|
|
attributeProvider := &MockAttributeProvider{}
|
|
|
|
suite.accessControl, err = NewAccessControlMatrix(suite.config, policyEngine, attributeProvider, suite.auditLogger)
|
|
suite.Require().NoError(err)
|
|
}
|
|
|
|
// TestBasicEncryptionDecryption tests basic encryption and decryption functionality
|
|
func (suite *RoleCryptoTestSuite) TestBasicEncryptionDecryption() {
|
|
// Create test context
|
|
address, err := ucxl.Parse("context://test/basic/encryption")
|
|
suite.Require().NoError(err)
|
|
|
|
testContext := &slurpContext.ContextNode{
|
|
Path: "/test/basic/encryption",
|
|
UCXLAddress: address,
|
|
Summary: "Test context for basic encryption",
|
|
Purpose: "Testing encryption functionality",
|
|
Technologies: []string{"go", "crypto"},
|
|
Tags: []string{"test", "encryption"},
|
|
Insights: []string{"This is a test insight"},
|
|
GeneratedAt: time.Now(),
|
|
RAGConfidence: 0.95,
|
|
EncryptedFor: []string{"backend_developer"},
|
|
AccessLevel: slurpContext.AccessMedium,
|
|
}
|
|
|
|
// Test encryption
|
|
targetRoles := []string{"backend_developer", "senior_architect"}
|
|
compartmentTags := []string{"development", "testing"}
|
|
|
|
encryptedData, err := suite.roleCrypto.EncryptContextForRoles(testContext, targetRoles, compartmentTags)
|
|
suite.Require().NoError(err)
|
|
suite.NotNil(encryptedData)
|
|
suite.Equal(address, encryptedData.UCXLAddress)
|
|
suite.NotEmpty(encryptedData.EncryptedLayers)
|
|
suite.NotNil(encryptedData.AccessControlMeta)
|
|
|
|
// Test decryption
|
|
decryptedContext, err := suite.roleCrypto.DecryptContextForRole(encryptedData, "backend_developer")
|
|
suite.Require().NoError(err)
|
|
suite.NotNil(decryptedContext)
|
|
suite.Equal(testContext.Summary, decryptedContext.Summary)
|
|
suite.Equal(testContext.Purpose, decryptedContext.Purpose)
|
|
|
|
// Verify audit logging
|
|
suite.True(len(suite.auditLogger.accessLogs) > 0)
|
|
}
|
|
|
|
// TestRoleBasedAccess tests role-based access control
|
|
func (suite *RoleCryptoTestSuite) TestRoleBasedAccess() {
|
|
address, err := ucxl.Parse("context://test/rbac/access")
|
|
suite.Require().NoError(err)
|
|
|
|
testContext := &slurpContext.ContextNode{
|
|
Path: "/test/rbac/access",
|
|
UCXLAddress: address,
|
|
Summary: "Test context for RBAC",
|
|
Purpose: "Testing role-based access control",
|
|
Technologies: []string{"security", "rbac"},
|
|
Tags: []string{"test", "security"},
|
|
EncryptedFor: []string{"senior_architect"},
|
|
AccessLevel: slurpContext.AccessHigh,
|
|
GeneratedAt: time.Now(),
|
|
RAGConfidence: 0.9,
|
|
}
|
|
|
|
// Encrypt for high-privilege role only
|
|
encryptedData, err := suite.roleCrypto.EncryptContextForRoles(testContext, []string{"senior_architect"}, []string{"security"})
|
|
suite.Require().NoError(err)
|
|
|
|
// Test access with authorized role
|
|
decryptedContext, err := suite.roleCrypto.DecryptContextForRole(encryptedData, "senior_architect")
|
|
suite.Require().NoError(err)
|
|
suite.NotNil(decryptedContext)
|
|
|
|
// Test access with unauthorized role (should fail)
|
|
_, err = suite.roleCrypto.DecryptContextForRole(encryptedData, "intern")
|
|
suite.Error(err)
|
|
suite.Contains(err.Error(), "access denied")
|
|
}
|
|
|
|
// TestMultiLayerEncryption tests multi-layer encryption with different access levels
|
|
func (suite *RoleCryptoTestSuite) TestMultiLayerEncryption() {
|
|
address, err := ucxl.Parse("context://test/multilayer/encryption")
|
|
suite.Require().NoError(err)
|
|
|
|
testContext := &slurpContext.ContextNode{
|
|
Path: "/test/multilayer/encryption",
|
|
UCXLAddress: address,
|
|
Summary: "Test context for multi-layer encryption",
|
|
Purpose: "Testing layered encryption",
|
|
Technologies: []string{"encryption", "security"},
|
|
Tags: []string{"test", "multilayer"},
|
|
Insights: []string{"Multi-layer security insight", "Advanced encryption insight"},
|
|
EncryptedFor: []string{"backend_developer", "senior_architect", "devops_engineer"},
|
|
AccessLevel: slurpContext.AccessMedium,
|
|
GeneratedAt: time.Now(),
|
|
RAGConfidence: 0.85,
|
|
}
|
|
|
|
// Encrypt for multiple roles with different access levels
|
|
targetRoles := []string{"backend_developer", "senior_architect", "devops_engineer"}
|
|
encryptedData, err := suite.roleCrypto.EncryptContextForRoles(testContext, targetRoles, []string{"development"})
|
|
suite.Require().NoError(err)
|
|
|
|
// Verify multiple encryption layers
|
|
suite.True(len(encryptedData.EncryptedLayers) > 0)
|
|
suite.NotEmpty(encryptedData.KeyFingerprints)
|
|
|
|
// Test decryption with different roles
|
|
for _, role := range targetRoles {
|
|
decryptedContext, err := suite.roleCrypto.DecryptContextForRole(encryptedData, role)
|
|
suite.Require().NoError(err, "Failed to decrypt for role: %s", role)
|
|
suite.NotNil(decryptedContext)
|
|
suite.Equal(testContext.Summary, decryptedContext.Summary)
|
|
}
|
|
}
|
|
|
|
// TestRoleBasedFiltering tests role-specific context filtering
|
|
func (suite *RoleCryptoTestSuite) TestRoleBasedFiltering() {
|
|
address, err := ucxl.Parse("context://test/filtering/context")
|
|
suite.Require().NoError(err)
|
|
|
|
testContext := &slurpContext.ContextNode{
|
|
Path: "/test/filtering/context",
|
|
UCXLAddress: address,
|
|
Summary: "Test context for filtering",
|
|
Purpose: "Testing role-based filtering",
|
|
Technologies: []string{"frontend", "backend", "database"},
|
|
Tags: []string{"test", "filtering"},
|
|
Insights: []string{"Frontend insight", "Backend insight", "Database insight"},
|
|
EncryptedFor: []string{"frontend_developer", "backend_developer"},
|
|
AccessLevel: slurpContext.AccessMedium,
|
|
GeneratedAt: time.Now(),
|
|
RAGConfidence: 0.8,
|
|
}
|
|
|
|
encryptedData, err := suite.roleCrypto.EncryptContextForRoles(testContext, []string{"frontend_developer", "backend_developer"}, []string{"development"})
|
|
suite.Require().NoError(err)
|
|
|
|
// Test frontend developer access (should get frontend-specific insights)
|
|
frontendContext, err := suite.roleCrypto.DecryptContextForRole(encryptedData, "frontend_developer")
|
|
suite.Require().NoError(err)
|
|
suite.Contains(strings.Join(frontendContext.Insights, " "), "Frontend")
|
|
|
|
// Test backend developer access (should get backend-specific insights)
|
|
backendContext, err := suite.roleCrypto.DecryptContextForRole(encryptedData, "backend_developer")
|
|
suite.Require().NoError(err)
|
|
suite.Contains(strings.Join(backendContext.Insights, " "), "Backend")
|
|
}
|
|
|
|
// TestKeyManagement tests key generation and management
|
|
func (suite *RoleCryptoTestSuite) TestKeyManagement() {
|
|
// Test key generation
|
|
keyPair, err := suite.keyManager.GenerateRoleKey("test_role", "age-x25519")
|
|
suite.Require().NoError(err)
|
|
suite.NotNil(keyPair)
|
|
suite.NotEmpty(keyPair.PublicKey)
|
|
suite.NotEmpty(keyPair.PrivateKey)
|
|
suite.True(strings.HasPrefix(keyPair.PublicKey, "age1"))
|
|
|
|
// Test key retrieval
|
|
metadata, err := suite.keyManager.GetKeyMetadata(&KeyFilter{RoleID: "test_role"})
|
|
suite.Require().NoError(err)
|
|
suite.True(len(metadata) > 0)
|
|
|
|
// Test key integrity verification
|
|
keyID := fmt.Sprintf("test_role_age-x25519_v%d", keyPair.Version)
|
|
verificationResult, err := suite.keyManager.VerifyKeyIntegrity(keyID)
|
|
suite.Require().NoError(err)
|
|
suite.NotNil(verificationResult)
|
|
suite.True(verificationResult.IntegrityOK)
|
|
}
|
|
|
|
// TestKeyRotation tests automatic key rotation
|
|
func (suite *RoleCryptoTestSuite) TestKeyRotation() {
|
|
// Create initial key
|
|
originalKeyPair, err := suite.keyManager.GenerateRoleKey("rotation_test_role", "age-x25519")
|
|
suite.Require().NoError(err)
|
|
|
|
// Perform key rotation
|
|
rotationResult, err := suite.keyManager.RotateKey("rotation_test_role", "test_rotation")
|
|
suite.Require().NoError(err)
|
|
suite.NotNil(rotationResult)
|
|
suite.Contains(rotationResult.RotatedRoles, "rotation_test_role")
|
|
suite.NotEmpty(rotationResult.NewKeys)
|
|
suite.NotEmpty(rotationResult.RevokedKeys)
|
|
|
|
// Verify new key is different from original
|
|
newKey := rotationResult.NewKeys["rotation_test_role"]
|
|
suite.NotEqual(originalKeyPair.PublicKey, string(newKey.KeyData))
|
|
|
|
// Verify audit logging
|
|
suite.True(len(suite.auditLogger.keyRotations) > 0)
|
|
rotation := suite.auditLogger.keyRotations[len(suite.auditLogger.keyRotations)-1]
|
|
suite.Equal("test_rotation", rotation.Reason)
|
|
suite.True(rotation.Success)
|
|
}
|
|
|
|
// TestAccessControlMatrix tests the access control matrix functionality
|
|
func (suite *RoleCryptoTestSuite) TestAccessControlMatrix() {
|
|
ctx := context.Background()
|
|
|
|
// Create access request
|
|
request := &AccessRequest{
|
|
RequestID: "test_request_001",
|
|
Timestamp: time.Now(),
|
|
UserID: "test_user",
|
|
Roles: []string{"backend_developer"},
|
|
Resource: "context://test/access/resource",
|
|
ResourceType: "context",
|
|
Action: "read",
|
|
ActionType: "data_access",
|
|
SessionID: "test_session_001",
|
|
IPAddress: "192.168.1.100",
|
|
UserAgent: "TestAgent/1.0",
|
|
Priority: 1,
|
|
Justification: "Testing access control",
|
|
Metadata: make(map[string]interface{}),
|
|
}
|
|
|
|
// Test access evaluation
|
|
decision, err := suite.accessControl.CheckAccess(ctx, request)
|
|
suite.Require().NoError(err)
|
|
suite.NotNil(decision)
|
|
suite.Equal(DecisionPermit, decision.Decision)
|
|
suite.True(decision.ConfidenceScore > 0)
|
|
suite.True(decision.EvaluationTime > 0)
|
|
|
|
// Test unauthorized access
|
|
unauthorizedRequest := &AccessRequest{
|
|
RequestID: "test_request_002",
|
|
Timestamp: time.Now(),
|
|
UserID: "unauthorized_user",
|
|
Roles: []string{"intern"},
|
|
Resource: "context://test/sensitive/resource",
|
|
ResourceType: "context",
|
|
Action: "write",
|
|
ActionType: "data_modification",
|
|
SessionID: "test_session_002",
|
|
IPAddress: "192.168.1.200",
|
|
UserAgent: "TestAgent/1.0",
|
|
Priority: 1,
|
|
Justification: "Testing unauthorized access",
|
|
Metadata: make(map[string]interface{}),
|
|
}
|
|
|
|
unauthorizedDecision, err := suite.accessControl.CheckAccess(ctx, unauthorizedRequest)
|
|
suite.Require().NoError(err)
|
|
suite.Equal(DecisionDeny, unauthorizedDecision.Decision)
|
|
}
|
|
|
|
// TestBypassTokens tests bypass token functionality
|
|
func (suite *RoleCryptoTestSuite) TestBypassTokens() {
|
|
// Create bypass token
|
|
token, err := suite.accessControl.CreateBypassToken(
|
|
"admin_user",
|
|
"Emergency maintenance",
|
|
[]string{"context://emergency/*"},
|
|
1*time.Hour,
|
|
5,
|
|
)
|
|
suite.Require().NoError(err)
|
|
suite.NotNil(token)
|
|
suite.Equal(BypassTokenStatusActive, token.Status)
|
|
suite.Equal(0, token.UsageCount)
|
|
|
|
// Test access with bypass token
|
|
ctx := context.Background()
|
|
request := &AccessRequest{
|
|
RequestID: "bypass_test_001",
|
|
Timestamp: time.Now(),
|
|
UserID: "regular_user",
|
|
Roles: []string{"intern"},
|
|
Resource: "context://emergency/system",
|
|
Action: "read",
|
|
Metadata: map[string]interface{}{"bypass_token": token.TokenID},
|
|
}
|
|
|
|
decision, err := suite.accessControl.CheckAccess(ctx, request)
|
|
suite.Require().NoError(err)
|
|
suite.Equal(DecisionPermit, decision.Decision)
|
|
suite.Contains(decision.Reason, "Bypass token")
|
|
|
|
// Verify token usage was recorded
|
|
suite.Equal(1, token.UsageCount)
|
|
}
|
|
|
|
// TestSecurityMetrics tests security metrics collection
|
|
func (suite *RoleCryptoTestSuite) TestSecurityMetrics() {
|
|
// Get role crypto metrics
|
|
metrics := suite.roleCrypto.GetSecurityMetrics()
|
|
suite.NotNil(metrics)
|
|
suite.Contains(metrics, "total_roles")
|
|
suite.Contains(metrics, "security_score")
|
|
|
|
// Get access control metrics
|
|
acMetrics := suite.accessControl.GetAccessControlMetrics()
|
|
suite.NotNil(acMetrics)
|
|
suite.Contains(acMetrics, "total_evaluations")
|
|
suite.Contains(acMetrics, "enforcement_mode")
|
|
|
|
// Get key management security status
|
|
keyStatus := suite.keyManager.GetSecurityStatus()
|
|
suite.NotNil(keyStatus)
|
|
suite.Contains([]string{"healthy", "warning", "degraded", "critical"}, keyStatus.OverallHealth)
|
|
suite.True(keyStatus.SecurityScore >= 0 && keyStatus.SecurityScore <= 1)
|
|
}
|
|
|
|
// TestComplianceFeatures tests compliance and audit features
|
|
func (suite *RoleCryptoTestSuite) TestComplianceFeatures() {
|
|
// Test audit trail retrieval
|
|
criteria := &AuditCriteria{
|
|
StartTime: &[]time.Time{time.Now().Add(-1 * time.Hour)}[0],
|
|
EndTime: &[]time.Time{time.Now()}[0],
|
|
UserID: "test_user",
|
|
Limit: 100,
|
|
}
|
|
|
|
auditTrail, err := suite.auditLogger.GetAuditTrail(criteria)
|
|
suite.Require().NoError(err)
|
|
suite.NotNil(auditTrail)
|
|
|
|
// Verify audit events have required fields
|
|
if len(auditTrail) > 0 {
|
|
event := auditTrail[0]
|
|
suite.NotEmpty(event.EventID)
|
|
suite.NotEmpty(event.EventType)
|
|
suite.NotEmpty(event.UserID)
|
|
suite.NotZero(event.Timestamp)
|
|
}
|
|
}
|
|
|
|
// TestErrorHandling tests error handling and edge cases
|
|
func (suite *RoleCryptoTestSuite) TestErrorHandling() {
|
|
// Test encryption with invalid context
|
|
invalidContext := &slurpContext.ContextNode{
|
|
Path: "", // Invalid: empty path
|
|
Summary: "", // Invalid: empty summary
|
|
GeneratedAt: time.Now(),
|
|
}
|
|
|
|
_, err := suite.roleCrypto.EncryptContextForRoles(invalidContext, []string{"backend_developer"}, []string{})
|
|
suite.Error(err)
|
|
suite.Contains(err.Error(), "invalid context")
|
|
|
|
// Test decryption with invalid role
|
|
address, _ := ucxl.Parse("context://test/valid/context")
|
|
validContext := &slurpContext.ContextNode{
|
|
Path: "/test/valid/context",
|
|
UCXLAddress: address,
|
|
Summary: "Valid test context",
|
|
Purpose: "Testing error handling",
|
|
GeneratedAt: time.Now(),
|
|
RAGConfidence: 0.8,
|
|
EncryptedFor: []string{"backend_developer"},
|
|
AccessLevel: slurpContext.AccessMedium,
|
|
}
|
|
|
|
encryptedData, err := suite.roleCrypto.EncryptContextForRoles(validContext, []string{"backend_developer"}, []string{})
|
|
suite.Require().NoError(err)
|
|
|
|
_, err = suite.roleCrypto.DecryptContextForRole(encryptedData, "non_existent_role")
|
|
suite.Error(err)
|
|
suite.Contains(err.Error(), "access denied")
|
|
|
|
// Test key generation with invalid parameters
|
|
_, err = suite.keyManager.GenerateRoleKey("", "invalid_type")
|
|
suite.Error(err)
|
|
}
|
|
|
|
// TestPerformance tests performance characteristics
|
|
func (suite *RoleCryptoTestSuite) TestPerformance() {
|
|
// Create test context
|
|
address, _ := ucxl.Parse("context://test/performance/benchmark")
|
|
testContext := &slurpContext.ContextNode{
|
|
Path: "/test/performance/benchmark",
|
|
UCXLAddress: address,
|
|
Summary: "Performance test context",
|
|
Purpose: "Testing encryption performance",
|
|
Technologies: []string{"performance", "crypto"},
|
|
Tags: []string{"test", "benchmark"},
|
|
Insights: make([]string, 100), // Large insights array
|
|
GeneratedAt: time.Now(),
|
|
RAGConfidence: 0.9,
|
|
EncryptedFor: []string{"backend_developer"},
|
|
AccessLevel: slurpContext.AccessMedium,
|
|
}
|
|
|
|
// Fill insights with test data
|
|
for i := 0; i < 100; i++ {
|
|
testContext.Insights[i] = fmt.Sprintf("Performance test insight #%d", i+1)
|
|
}
|
|
|
|
// Benchmark encryption
|
|
start := time.Now()
|
|
encryptedData, err := suite.roleCrypto.EncryptContextForRoles(testContext, []string{"backend_developer"}, []string{"performance"})
|
|
encryptionTime := time.Since(start)
|
|
|
|
suite.Require().NoError(err)
|
|
suite.True(encryptionTime < 100*time.Millisecond, "Encryption took too long: %v", encryptionTime)
|
|
|
|
// Benchmark decryption
|
|
start = time.Now()
|
|
_, err = suite.roleCrypto.DecryptContextForRole(encryptedData, "backend_developer")
|
|
decryptionTime := time.Since(start)
|
|
|
|
suite.Require().NoError(err)
|
|
suite.True(decryptionTime < 50*time.Millisecond, "Decryption took too long: %v", decryptionTime)
|
|
|
|
// Test concurrent operations
|
|
concurrentOps := 10
|
|
results := make(chan error, concurrentOps)
|
|
|
|
for i := 0; i < concurrentOps; i++ {
|
|
go func(index int) {
|
|
address, _ := ucxl.Parse(fmt.Sprintf("context://test/concurrent/%d", index))
|
|
ctx := &slurpContext.ContextNode{
|
|
Path: fmt.Sprintf("/test/concurrent/%d", index),
|
|
UCXLAddress: address,
|
|
Summary: fmt.Sprintf("Concurrent test context %d", index),
|
|
Purpose: "Testing concurrent operations",
|
|
GeneratedAt: time.Now(),
|
|
RAGConfidence: 0.8,
|
|
EncryptedFor: []string{"backend_developer"},
|
|
AccessLevel: slurpContext.AccessMedium,
|
|
}
|
|
|
|
encrypted, err := suite.roleCrypto.EncryptContextForRoles(ctx, []string{"backend_developer"}, []string{"concurrent"})
|
|
if err != nil {
|
|
results <- err
|
|
return
|
|
}
|
|
|
|
_, err = suite.roleCrypto.DecryptContextForRole(encrypted, "backend_developer")
|
|
results <- err
|
|
}(i)
|
|
}
|
|
|
|
// Wait for all operations to complete
|
|
for i := 0; i < concurrentOps; i++ {
|
|
err := <-results
|
|
suite.NoError(err, "Concurrent operation %d failed", i)
|
|
}
|
|
}
|
|
|
|
// TestSecurityVulnerabilities tests for common security vulnerabilities
|
|
func (suite *RoleCryptoTestSuite) TestSecurityVulnerabilities() {
|
|
// Test for timing attacks (encryption should take consistent time)
|
|
address, _ := ucxl.Parse("context://test/security/timing")
|
|
baseContext := &slurpContext.ContextNode{
|
|
Path: "/test/security/timing",
|
|
UCXLAddress: address,
|
|
Summary: "Timing attack test",
|
|
Purpose: "Testing for timing vulnerabilities",
|
|
GeneratedAt: time.Now(),
|
|
RAGConfidence: 0.8,
|
|
EncryptedFor: []string{"backend_developer"},
|
|
AccessLevel: slurpContext.AccessMedium,
|
|
}
|
|
|
|
// Measure encryption times for different content sizes
|
|
var times []time.Duration
|
|
for i := 0; i < 10; i++ {
|
|
testContext := *baseContext
|
|
testContext.Insights = make([]string, i*10) // Varying content size
|
|
for j := range testContext.Insights {
|
|
testContext.Insights[j] = fmt.Sprintf("Insight %d", j)
|
|
}
|
|
|
|
start := time.Now()
|
|
_, err := suite.roleCrypto.EncryptContextForRoles(&testContext, []string{"backend_developer"}, []string{"timing"})
|
|
duration := time.Since(start)
|
|
|
|
suite.Require().NoError(err)
|
|
times = append(times, duration)
|
|
}
|
|
|
|
// Check that times don't vary too much (basic timing attack protection)
|
|
maxTime := times[0]
|
|
minTime := times[0]
|
|
for _, t := range times {
|
|
if t > maxTime {
|
|
maxTime = t
|
|
}
|
|
if t < minTime {
|
|
minTime = t
|
|
}
|
|
}
|
|
|
|
// Times should not vary by more than 100% (basic check)
|
|
variance := float64(maxTime-minTime) / float64(minTime)
|
|
suite.True(variance < 2.0, "Encryption times vary too much: %v", variance)
|
|
|
|
// Test for privilege escalation (lower privilege role shouldn't access higher privilege content)
|
|
highPrivContext := &slurpContext.ContextNode{
|
|
Path: "/test/security/privilege",
|
|
UCXLAddress: address,
|
|
Summary: "High privilege content",
|
|
Purpose: "Testing privilege escalation protection",
|
|
GeneratedAt: time.Now(),
|
|
RAGConfidence: 0.9,
|
|
EncryptedFor: []string{"senior_architect"},
|
|
AccessLevel: slurpContext.AccessCritical,
|
|
}
|
|
|
|
encryptedHighPriv, err := suite.roleCrypto.EncryptContextForRoles(highPrivContext, []string{"senior_architect"}, []string{"security"})
|
|
suite.Require().NoError(err)
|
|
|
|
// Attempt access with lower privilege role
|
|
_, err = suite.roleCrypto.DecryptContextForRole(encryptedHighPriv, "intern")
|
|
suite.Error(err, "Lower privilege role should not access higher privilege content")
|
|
|
|
// Test for information leakage in error messages
|
|
suite.NotContains(err.Error(), "senior_architect", "Error message should not leak role information")
|
|
suite.NotContains(err.Error(), highPrivContext.Summary, "Error message should not leak content information")
|
|
}
|
|
|
|
// BenchmarkEncryption benchmarks encryption operations
|
|
func BenchmarkEncryption(b *testing.B) {
|
|
// Setup
|
|
config := &config.Config{
|
|
Agent: config.Agent{ID: "bench_agent", Role: "backend_developer"},
|
|
}
|
|
auditLogger := &MockAuditLogger{}
|
|
ageCrypto := NewAgeCrypto(config)
|
|
adminKeyManager := NewAdminKeyManager(config, "bench_node")
|
|
|
|
roleCrypto, err := NewRoleCrypto(config, ageCrypto, adminKeyManager, auditLogger)
|
|
require.NoError(b, err)
|
|
|
|
address, _ := ucxl.Parse("context://benchmark/encryption")
|
|
testContext := &slurpContext.ContextNode{
|
|
Path: "/benchmark/encryption",
|
|
UCXLAddress: address,
|
|
Summary: "Benchmark context for encryption testing",
|
|
Purpose: "Performance testing of encryption operations",
|
|
Technologies: []string{"crypto", "benchmark"},
|
|
Tags: []string{"benchmark", "performance"},
|
|
Insights: []string{"Benchmark insight 1", "Benchmark insight 2"},
|
|
GeneratedAt: time.Now(),
|
|
RAGConfidence: 0.85,
|
|
EncryptedFor: []string{"backend_developer"},
|
|
AccessLevel: slurpContext.AccessMedium,
|
|
}
|
|
|
|
b.ResetTimer()
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
_, err := roleCrypto.EncryptContextForRoles(testContext, []string{"backend_developer"}, []string{"benchmark"})
|
|
if err != nil {
|
|
b.Fatalf("Encryption failed: %v", err)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// BenchmarkDecryption benchmarks decryption operations
|
|
func BenchmarkDecryption(b *testing.B) {
|
|
// Setup
|
|
config := &config.Config{
|
|
Agent: config.Agent{ID: "bench_agent", Role: "backend_developer"},
|
|
}
|
|
auditLogger := &MockAuditLogger{}
|
|
ageCrypto := NewAgeCrypto(config)
|
|
adminKeyManager := NewAdminKeyManager(config, "bench_node")
|
|
|
|
roleCrypto, err := NewRoleCrypto(config, ageCrypto, adminKeyManager, auditLogger)
|
|
require.NoError(b, err)
|
|
|
|
address, _ := ucxl.Parse("context://benchmark/decryption")
|
|
testContext := &slurpContext.ContextNode{
|
|
Path: "/benchmark/decryption",
|
|
UCXLAddress: address,
|
|
Summary: "Benchmark context for decryption testing",
|
|
Purpose: "Performance testing of decryption operations",
|
|
Technologies: []string{"crypto", "benchmark"},
|
|
Tags: []string{"benchmark", "performance"},
|
|
Insights: []string{"Benchmark insight 1", "Benchmark insight 2"},
|
|
GeneratedAt: time.Now(),
|
|
RAGConfidence: 0.85,
|
|
EncryptedFor: []string{"backend_developer"},
|
|
AccessLevel: slurpContext.AccessMedium,
|
|
}
|
|
|
|
// Pre-encrypt context for benchmarking decryption
|
|
encryptedData, err := roleCrypto.EncryptContextForRoles(testContext, []string{"backend_developer"}, []string{"benchmark"})
|
|
require.NoError(b, err)
|
|
|
|
b.ResetTimer()
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
_, err := roleCrypto.DecryptContextForRole(encryptedData, "backend_developer")
|
|
if err != nil {
|
|
b.Fatalf("Decryption failed: %v", err)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// BenchmarkAccessControl benchmarks access control evaluation
|
|
func BenchmarkAccessControl(b *testing.B) {
|
|
// Setup
|
|
config := &config.Config{
|
|
Agent: config.Agent{ID: "bench_agent", Role: "backend_developer"},
|
|
}
|
|
auditLogger := &MockAuditLogger{}
|
|
policyEngine := NewMockPolicyEngine()
|
|
attributeProvider := &MockAttributeProvider{}
|
|
|
|
accessControl, err := NewAccessControlMatrix(config, policyEngine, attributeProvider, auditLogger)
|
|
require.NoError(b, err)
|
|
|
|
ctx := context.Background()
|
|
request := &AccessRequest{
|
|
RequestID: "benchmark_request",
|
|
Timestamp: time.Now(),
|
|
UserID: "bench_user",
|
|
Roles: []string{"backend_developer"},
|
|
Resource: "context://benchmark/access",
|
|
ResourceType: "context",
|
|
Action: "read",
|
|
ActionType: "data_access",
|
|
SessionID: "bench_session",
|
|
IPAddress: "192.168.1.100",
|
|
UserAgent: "BenchmarkAgent/1.0",
|
|
Priority: 1,
|
|
Metadata: make(map[string]interface{}),
|
|
}
|
|
|
|
b.ResetTimer()
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
_, err := accessControl.CheckAccess(ctx, request)
|
|
if err != nil {
|
|
b.Fatalf("Access control evaluation failed: %v", err)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// TestMain sets up and tears down the test suite
|
|
func TestMain(m *testing.M) {
|
|
// Setup any global test resources here
|
|
|
|
// Run tests
|
|
code := m.Run()
|
|
|
|
// Cleanup any global test resources here
|
|
|
|
// Exit with the same code as the test run
|
|
fmt.Printf("Test suite completed with exit code: %d\n", code)
|
|
}
|
|
|
|
// Run the test suite
|
|
func TestRoleCryptoTestSuite(t *testing.T) {
|
|
suite.Run(t, new(RoleCryptoTestSuite))
|
|
} |