chore: clean .gitignore cache
This commit is contained in:
@@ -1,167 +0,0 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Wikid82/charon/backend/internal/models"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// SecurityHeadersService manages security header profiles
|
||||
type SecurityHeadersService struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewSecurityHeadersService creates a new security headers service
|
||||
func NewSecurityHeadersService(db *gorm.DB) *SecurityHeadersService {
|
||||
return &SecurityHeadersService{db: db}
|
||||
}
|
||||
|
||||
// GetPresets returns the built-in presets
|
||||
func (s *SecurityHeadersService) GetPresets() []models.SecurityHeaderProfile {
|
||||
return []models.SecurityHeaderProfile{
|
||||
{
|
||||
UUID: "preset-basic",
|
||||
Name: "Basic Security",
|
||||
PresetType: "basic",
|
||||
IsPreset: true,
|
||||
Description: "Essential security headers for most websites. Safe defaults that won't break functionality.",
|
||||
HSTSEnabled: true,
|
||||
HSTSMaxAge: 31536000, // 1 year
|
||||
HSTSIncludeSubdomains: false,
|
||||
HSTSPreload: false,
|
||||
CSPEnabled: false, // CSP can break sites
|
||||
XFrameOptions: "SAMEORIGIN",
|
||||
XContentTypeOptions: true,
|
||||
ReferrerPolicy: "strict-origin-when-cross-origin",
|
||||
XSSProtection: true,
|
||||
SecurityScore: 65,
|
||||
},
|
||||
{
|
||||
UUID: "preset-api-friendly",
|
||||
Name: "API-Friendly",
|
||||
PresetType: "api-friendly",
|
||||
IsPreset: true,
|
||||
Description: "Optimized for mobile apps and API access (Radarr, Plex, Home Assistant). Strong transport security without breaking API compatibility.",
|
||||
HSTSEnabled: true,
|
||||
HSTSMaxAge: 31536000, // 1 year
|
||||
HSTSIncludeSubdomains: false,
|
||||
HSTSPreload: false,
|
||||
CSPEnabled: false, // APIs don't need CSP
|
||||
XFrameOptions: "", // Allow WebViews
|
||||
XContentTypeOptions: true,
|
||||
ReferrerPolicy: "strict-origin-when-cross-origin",
|
||||
PermissionsPolicy: "", // Allow all permissions
|
||||
CrossOriginOpenerPolicy: "", // Allow OAuth popups
|
||||
CrossOriginResourcePolicy: "cross-origin", // KEY: Allow cross-origin access
|
||||
CrossOriginEmbedderPolicy: "", // Don't require CORP
|
||||
XSSProtection: true,
|
||||
CacheControlNoStore: false,
|
||||
SecurityScore: 70,
|
||||
},
|
||||
{
|
||||
UUID: "preset-strict",
|
||||
Name: "Strict Security",
|
||||
PresetType: "strict",
|
||||
IsPreset: true,
|
||||
Description: "Strong security for applications handling sensitive data. May require CSP adjustments.",
|
||||
HSTSEnabled: true,
|
||||
HSTSMaxAge: 31536000,
|
||||
HSTSIncludeSubdomains: true,
|
||||
HSTSPreload: false,
|
||||
CSPEnabled: true,
|
||||
CSPDirectives: `{"default-src":["'self'"],"script-src":["'self'"],"style-src":["'self'","'unsafe-inline'"],"img-src":["'self'","data:","https:"],"font-src":["'self'","data:"],"connect-src":["'self'"],"frame-src":["'none'"],"object-src":["'none'"]}`,
|
||||
XFrameOptions: "DENY",
|
||||
XContentTypeOptions: true,
|
||||
ReferrerPolicy: "strict-origin-when-cross-origin",
|
||||
PermissionsPolicy: `[{"feature":"camera","allowlist":[]},{"feature":"microphone","allowlist":[]},{"feature":"geolocation","allowlist":[]}]`,
|
||||
XSSProtection: true,
|
||||
CrossOriginOpenerPolicy: "same-origin",
|
||||
CrossOriginResourcePolicy: "same-origin",
|
||||
SecurityScore: 85,
|
||||
},
|
||||
{
|
||||
UUID: "preset-paranoid",
|
||||
Name: "Paranoid Security",
|
||||
PresetType: "paranoid",
|
||||
IsPreset: true,
|
||||
Description: "Maximum security for high-risk applications. May break some functionality. Test thoroughly.",
|
||||
HSTSEnabled: true,
|
||||
HSTSMaxAge: 63072000, // 2 years
|
||||
HSTSIncludeSubdomains: true,
|
||||
HSTSPreload: true,
|
||||
CSPEnabled: true,
|
||||
CSPDirectives: `{"default-src":["'none'"],"script-src":["'self'"],"style-src":["'self'"],"img-src":["'self'"],"font-src":["'self'"],"connect-src":["'self'"],"frame-src":["'none'"],"object-src":["'none'"],"base-uri":["'self'"],"form-action":["'self'"],"frame-ancestors":["'none'"]}`,
|
||||
XFrameOptions: "DENY",
|
||||
XContentTypeOptions: true,
|
||||
ReferrerPolicy: "no-referrer",
|
||||
PermissionsPolicy: `[{"feature":"camera","allowlist":[]},{"feature":"microphone","allowlist":[]},{"feature":"geolocation","allowlist":[]},{"feature":"payment","allowlist":[]},{"feature":"usb","allowlist":[]}]`,
|
||||
XSSProtection: true,
|
||||
CrossOriginOpenerPolicy: "same-origin",
|
||||
CrossOriginResourcePolicy: "same-origin",
|
||||
CrossOriginEmbedderPolicy: "require-corp",
|
||||
CacheControlNoStore: true,
|
||||
SecurityScore: 100,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// EnsurePresetsExist creates default presets if they don't exist
|
||||
func (s *SecurityHeadersService) EnsurePresetsExist() error {
|
||||
presets := s.GetPresets()
|
||||
|
||||
for _, preset := range presets {
|
||||
var existing models.SecurityHeaderProfile
|
||||
err := s.db.Where("uuid = ?", preset.UUID).First(&existing).Error
|
||||
|
||||
switch {
|
||||
case err == gorm.ErrRecordNotFound:
|
||||
// Create preset with a fresh UUID for the ID field
|
||||
if err := s.db.Create(&preset).Error; err != nil {
|
||||
return fmt.Errorf("failed to create preset %s: %w", preset.Name, err)
|
||||
}
|
||||
case err != nil:
|
||||
return fmt.Errorf("failed to check preset %s: %w", preset.Name, err)
|
||||
default:
|
||||
// Update existing preset to ensure it has latest values
|
||||
preset.ID = existing.ID // Keep the existing ID
|
||||
if err := s.db.Save(&preset).Error; err != nil {
|
||||
return fmt.Errorf("failed to update preset %s: %w", preset.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ApplyPreset creates a new profile based on a preset
|
||||
func (s *SecurityHeadersService) ApplyPreset(presetType, name string) (*models.SecurityHeaderProfile, error) {
|
||||
presets := s.GetPresets()
|
||||
|
||||
var selectedPreset *models.SecurityHeaderProfile
|
||||
for i := range presets {
|
||||
if presets[i].PresetType == presetType {
|
||||
selectedPreset = &presets[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if selectedPreset == nil {
|
||||
return nil, fmt.Errorf("preset type %s not found", presetType)
|
||||
}
|
||||
|
||||
// Create a copy with custom name and UUID
|
||||
newProfile := *selectedPreset
|
||||
newProfile.ID = 0 // Clear ID so GORM creates a new record
|
||||
newProfile.UUID = uuid.New().String()
|
||||
newProfile.Name = name
|
||||
newProfile.IsPreset = false // User-created profiles are not presets
|
||||
newProfile.PresetType = "" // Clear preset type for custom profiles
|
||||
|
||||
if err := s.db.Create(&newProfile).Error; err != nil {
|
||||
return nil, fmt.Errorf("failed to create profile from preset: %w", err)
|
||||
}
|
||||
|
||||
return &newProfile, nil
|
||||
}
|
||||
Reference in New Issue
Block a user