245 lines
6.7 KiB
Go
245 lines
6.7 KiB
Go
package models
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
func setupSecurityHeaderProfileDB(t *testing.T) *gorm.DB {
|
|
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
|
|
assert.NoError(t, err)
|
|
|
|
err = db.AutoMigrate(&SecurityHeaderProfile{})
|
|
assert.NoError(t, err)
|
|
|
|
return db
|
|
}
|
|
|
|
func TestSecurityHeaderProfile_Create(t *testing.T) {
|
|
db := setupSecurityHeaderProfileDB(t)
|
|
|
|
profile := SecurityHeaderProfile{
|
|
UUID: uuid.New().String(),
|
|
Name: "Test Profile",
|
|
HSTSEnabled: true,
|
|
HSTSMaxAge: 31536000,
|
|
HSTSIncludeSubdomains: true,
|
|
HSTSPreload: false,
|
|
CSPEnabled: false,
|
|
XFrameOptions: "DENY",
|
|
XContentTypeOptions: true,
|
|
ReferrerPolicy: "strict-origin-when-cross-origin",
|
|
XSSProtection: true,
|
|
SecurityScore: 65,
|
|
IsPreset: false,
|
|
}
|
|
|
|
err := db.Create(&profile).Error
|
|
assert.NoError(t, err)
|
|
assert.NotZero(t, profile.ID)
|
|
}
|
|
|
|
func TestSecurityHeaderProfile_JSONSerialization(t *testing.T) {
|
|
profile := SecurityHeaderProfile{
|
|
ID: 1,
|
|
UUID: "test-uuid",
|
|
Name: "Test Profile",
|
|
HSTSEnabled: true,
|
|
HSTSMaxAge: 31536000,
|
|
HSTSIncludeSubdomains: true,
|
|
XFrameOptions: "DENY",
|
|
SecurityScore: 85,
|
|
}
|
|
|
|
data, err := json.Marshal(profile)
|
|
assert.NoError(t, err)
|
|
assert.Contains(t, string(data), `"hsts_enabled":true`)
|
|
assert.Contains(t, string(data), `"hsts_max_age":31536000`)
|
|
assert.Contains(t, string(data), `"x_frame_options":"DENY"`)
|
|
|
|
var decoded SecurityHeaderProfile
|
|
err = json.Unmarshal(data, &decoded)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, profile.Name, decoded.Name)
|
|
assert.Equal(t, profile.HSTSEnabled, decoded.HSTSEnabled)
|
|
assert.Equal(t, profile.SecurityScore, decoded.SecurityScore)
|
|
}
|
|
|
|
func TestCSPDirective_JSONSerialization(t *testing.T) {
|
|
directive := CSPDirective{
|
|
Directive: "default-src",
|
|
Values: []string{"'self'", "https:"},
|
|
}
|
|
|
|
data, err := json.Marshal(directive)
|
|
assert.NoError(t, err)
|
|
assert.Contains(t, string(data), `"directive":"default-src"`)
|
|
assert.Contains(t, string(data), `"values":["'self'","https:"]`)
|
|
|
|
var decoded CSPDirective
|
|
err = json.Unmarshal(data, &decoded)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, directive.Directive, decoded.Directive)
|
|
assert.Equal(t, directive.Values, decoded.Values)
|
|
}
|
|
|
|
func TestPermissionsPolicyItem_JSONSerialization(t *testing.T) {
|
|
item := PermissionsPolicyItem{
|
|
Feature: "camera",
|
|
Allowlist: []string{"self"},
|
|
}
|
|
|
|
data, err := json.Marshal(item)
|
|
assert.NoError(t, err)
|
|
assert.Contains(t, string(data), `"feature":"camera"`)
|
|
assert.Contains(t, string(data), `"allowlist":["self"]`)
|
|
|
|
var decoded PermissionsPolicyItem
|
|
err = json.Unmarshal(data, &decoded)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, item.Feature, decoded.Feature)
|
|
assert.Equal(t, item.Allowlist, decoded.Allowlist)
|
|
}
|
|
|
|
func TestSecurityHeaderProfile_Defaults(t *testing.T) {
|
|
db := setupSecurityHeaderProfileDB(t)
|
|
|
|
profile := SecurityHeaderProfile{
|
|
UUID: uuid.New().String(),
|
|
Name: "Default Test",
|
|
}
|
|
|
|
err := db.Create(&profile).Error
|
|
assert.NoError(t, err)
|
|
|
|
// Reload to check defaults
|
|
var reloaded SecurityHeaderProfile
|
|
err = db.First(&reloaded, profile.ID).Error
|
|
assert.NoError(t, err)
|
|
|
|
assert.True(t, reloaded.HSTSEnabled)
|
|
assert.Equal(t, 31536000, reloaded.HSTSMaxAge)
|
|
assert.True(t, reloaded.HSTSIncludeSubdomains)
|
|
assert.False(t, reloaded.HSTSPreload)
|
|
assert.False(t, reloaded.CSPEnabled)
|
|
assert.Equal(t, "DENY", reloaded.XFrameOptions)
|
|
assert.True(t, reloaded.XContentTypeOptions)
|
|
assert.Equal(t, "strict-origin-when-cross-origin", reloaded.ReferrerPolicy)
|
|
assert.True(t, reloaded.XSSProtection)
|
|
assert.False(t, reloaded.CacheControlNoStore)
|
|
assert.Equal(t, 0, reloaded.SecurityScore)
|
|
assert.False(t, reloaded.IsPreset)
|
|
}
|
|
|
|
func TestSecurityHeaderProfile_UniqueUUID(t *testing.T) {
|
|
db := setupSecurityHeaderProfileDB(t)
|
|
|
|
testUUID := uuid.New().String()
|
|
|
|
profile1 := SecurityHeaderProfile{
|
|
UUID: testUUID,
|
|
Name: "Profile 1",
|
|
}
|
|
err := db.Create(&profile1).Error
|
|
assert.NoError(t, err)
|
|
|
|
profile2 := SecurityHeaderProfile{
|
|
UUID: testUUID,
|
|
Name: "Profile 2",
|
|
}
|
|
err = db.Create(&profile2).Error
|
|
assert.Error(t, err) // Should fail due to unique constraint
|
|
}
|
|
|
|
func TestSecurityHeaderProfile_CSPDirectivesStorage(t *testing.T) {
|
|
db := setupSecurityHeaderProfileDB(t)
|
|
|
|
cspDirectives := map[string][]string{
|
|
"default-src": {"'self'"},
|
|
"script-src": {"'self'", "'unsafe-inline'"},
|
|
"style-src": {"'self'", "https:"},
|
|
}
|
|
cspJSON, err := json.Marshal(cspDirectives)
|
|
assert.NoError(t, err)
|
|
|
|
profile := SecurityHeaderProfile{
|
|
UUID: uuid.New().String(),
|
|
Name: "CSP Test",
|
|
CSPEnabled: true,
|
|
CSPDirectives: string(cspJSON),
|
|
}
|
|
|
|
err = db.Create(&profile).Error
|
|
assert.NoError(t, err)
|
|
|
|
// Reload and verify
|
|
var reloaded SecurityHeaderProfile
|
|
err = db.First(&reloaded, profile.ID).Error
|
|
assert.NoError(t, err)
|
|
|
|
var decoded map[string][]string
|
|
err = json.Unmarshal([]byte(reloaded.CSPDirectives), &decoded)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, cspDirectives, decoded)
|
|
}
|
|
|
|
func TestSecurityHeaderProfile_PermissionsPolicyStorage(t *testing.T) {
|
|
db := setupSecurityHeaderProfileDB(t)
|
|
|
|
permissions := []PermissionsPolicyItem{
|
|
{Feature: "camera", Allowlist: []string{}},
|
|
{Feature: "microphone", Allowlist: []string{"self"}},
|
|
{Feature: "geolocation", Allowlist: []string{"*"}},
|
|
}
|
|
permJSON, err := json.Marshal(permissions)
|
|
assert.NoError(t, err)
|
|
|
|
profile := SecurityHeaderProfile{
|
|
UUID: uuid.New().String(),
|
|
Name: "Permissions Test",
|
|
PermissionsPolicy: string(permJSON),
|
|
}
|
|
|
|
err = db.Create(&profile).Error
|
|
assert.NoError(t, err)
|
|
|
|
// Reload and verify
|
|
var reloaded SecurityHeaderProfile
|
|
err = db.First(&reloaded, profile.ID).Error
|
|
assert.NoError(t, err)
|
|
|
|
var decoded []PermissionsPolicyItem
|
|
err = json.Unmarshal([]byte(reloaded.PermissionsPolicy), &decoded)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, permissions, decoded)
|
|
}
|
|
|
|
func TestSecurityHeaderProfile_PresetFields(t *testing.T) {
|
|
db := setupSecurityHeaderProfileDB(t)
|
|
|
|
profile := SecurityHeaderProfile{
|
|
UUID: uuid.New().String(),
|
|
Name: "Basic Security",
|
|
IsPreset: true,
|
|
PresetType: "basic",
|
|
Description: "Essential security headers for most websites",
|
|
}
|
|
|
|
err := db.Create(&profile).Error
|
|
assert.NoError(t, err)
|
|
|
|
// Reload
|
|
var reloaded SecurityHeaderProfile
|
|
err = db.First(&reloaded, profile.ID).Error
|
|
assert.NoError(t, err)
|
|
|
|
assert.True(t, reloaded.IsPreset)
|
|
assert.Equal(t, "basic", reloaded.PresetType)
|
|
assert.Equal(t, "Essential security headers for most websites", reloaded.Description)
|
|
}
|