- OAuth: use JWTService+SessionService, httpOnly cookies (VEZA-SEC-001) - Remove PasswordService.GenerateJWT (VEZA-SEC-002) - Hyperswitch webhook: mandatory verification, 500 if secret empty (VEZA-SEC-005) - Auth middleware: TokenBlacklist.IsBlacklisted check (VEZA-SEC-006) - Waveform: ValidateExecPath before exec (VEZA-SEC-007)
92 lines
2.9 KiB
Go
92 lines
2.9 KiB
Go
package api
|
|
|
|
import (
|
|
"bytes"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/zap"
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
|
|
"veza-backend-api/internal/config"
|
|
"veza-backend-api/internal/database"
|
|
"veza-backend-api/internal/metrics"
|
|
)
|
|
|
|
func setupWebhookTestRouter(t *testing.T, webhookSecret string) *gin.Engine {
|
|
t.Helper()
|
|
os.Setenv("ENABLE_CLAMAV", "false")
|
|
os.Setenv("CLAMAV_REQUIRED", "false")
|
|
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.New()
|
|
|
|
gormDB, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
|
|
require.NoError(t, err)
|
|
sqlDB, err := gormDB.DB()
|
|
require.NoError(t, err)
|
|
|
|
vezaDB := &database.Database{
|
|
DB: sqlDB,
|
|
GormDB: gormDB,
|
|
Logger: zap.NewNop(),
|
|
}
|
|
|
|
cfg := &config.Config{
|
|
HyperswitchWebhookSecret: webhookSecret,
|
|
JWTSecret: "test-jwt-secret-key-minimum-32-characters-long",
|
|
JWTIssuer: "veza-api",
|
|
JWTAudience: "veza-app",
|
|
Logger: zap.NewNop(),
|
|
RedisClient: nil,
|
|
ErrorMetrics: metrics.NewErrorMetrics(),
|
|
UploadDir: "uploads/test",
|
|
Env: "development",
|
|
Database: vezaDB,
|
|
CORSOrigins: []string{"*"},
|
|
HandlerTimeout: 30 * time.Second,
|
|
RateLimitLimit: 100,
|
|
RateLimitWindow: 60,
|
|
AuthRateLimitLoginAttempts: 10,
|
|
AuthRateLimitLoginWindow: 15,
|
|
}
|
|
require.NoError(t, cfg.InitServicesForTest())
|
|
require.NoError(t, cfg.InitMiddlewaresForTest())
|
|
|
|
apiRouter := NewAPIRouter(vezaDB, cfg)
|
|
require.NoError(t, apiRouter.Setup(router))
|
|
return router
|
|
}
|
|
|
|
// TestHyperswitchWebhook_EmptySecret_Returns500 verifies that when HyperswitchWebhookSecret
|
|
// is empty, the webhook handler returns 500 (VEZA-SEC-005).
|
|
func TestHyperswitchWebhook_EmptySecret_Returns500(t *testing.T) {
|
|
router := setupWebhookTestRouter(t, "")
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/webhooks/hyperswitch", bytes.NewReader([]byte(`{"test": true}`)))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusInternalServerError, w.Code, "empty webhook secret must return 500")
|
|
}
|
|
|
|
// TestHyperswitchWebhook_ValidSecret_InvalidSignature_Returns401 verifies that with a configured secret,
|
|
// invalid signature returns 401 (smoke test that verification path is exercised).
|
|
func TestHyperswitchWebhook_ValidSecret_InvalidSignature_Returns401(t *testing.T) {
|
|
router := setupWebhookTestRouter(t, "test-secret-at-least-32-chars-long")
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/webhooks/hyperswitch", bytes.NewReader([]byte(`{"test": true}`)))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusUnauthorized, w.Code, "invalid signature must return 401")
|
|
}
|