feat: implement HTTP Security Headers management (Issue #20)
Add comprehensive security header management system with reusable profiles, interactive builders, and security scoring. Features: - SecurityHeaderProfile model with 11+ header types - CRUD API with 10 endpoints (/api/v1/security/headers/*) - Caddy integration for automatic header injection - 3 built-in presets (Basic, Strict, Paranoid) - Security score calculator (0-100) with suggestions - Interactive CSP builder with validation - Permissions-Policy builder - Real-time security score preview - Per-host profile assignment Headers Supported: - HSTS with preload support - Content-Security-Policy with report-only mode - X-Frame-Options, X-Content-Type-Options - Referrer-Policy, Permissions-Policy - Cross-Origin-Opener/Resource/Embedder-Policy - X-XSS-Protection, Cache-Control security Implementation: - Backend: models, handlers, services (85% coverage) - Frontend: React components, hooks (87.46% coverage) - Tests: 1,163 total tests passing - Docs: Comprehensive feature documentation Closes #20
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { securityHeadersApi } from '../api/securityHeaders';
|
||||
import type { CreateProfileRequest, ApplyPresetRequest } from '../api/securityHeaders';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
export function useSecurityHeaderProfiles() {
|
||||
return useQuery({
|
||||
queryKey: ['securityHeaderProfiles'],
|
||||
queryFn: securityHeadersApi.listProfiles,
|
||||
});
|
||||
}
|
||||
|
||||
export function useSecurityHeaderProfile(id: number | string | undefined) {
|
||||
return useQuery({
|
||||
queryKey: ['securityHeaderProfile', id],
|
||||
queryFn: () => securityHeadersApi.getProfile(id!),
|
||||
enabled: !!id,
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateSecurityHeaderProfile() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (data: CreateProfileRequest) => securityHeadersApi.createProfile(data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['securityHeaderProfiles'] });
|
||||
toast.success('Security header profile created successfully');
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
toast.error(`Failed to create profile: ${error.message}`);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateSecurityHeaderProfile() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: number; data: Partial<CreateProfileRequest> }) =>
|
||||
securityHeadersApi.updateProfile(id, data),
|
||||
onSuccess: (_, variables) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['securityHeaderProfiles'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['securityHeaderProfile', variables.id] });
|
||||
toast.success('Security header profile updated successfully');
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
toast.error(`Failed to update profile: ${error.message}`);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteSecurityHeaderProfile() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => securityHeadersApi.deleteProfile(id),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['securityHeaderProfiles'] });
|
||||
toast.success('Security header profile deleted successfully');
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
toast.error(`Failed to delete profile: ${error.message}`);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useSecurityHeaderPresets() {
|
||||
return useQuery({
|
||||
queryKey: ['securityHeaderPresets'],
|
||||
queryFn: securityHeadersApi.getPresets,
|
||||
});
|
||||
}
|
||||
|
||||
export function useApplySecurityHeaderPreset() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (data: ApplyPresetRequest) => securityHeadersApi.applyPreset(data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['securityHeaderProfiles'] });
|
||||
toast.success('Preset applied successfully');
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
toast.error(`Failed to apply preset: ${error.message}`);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useCalculateSecurityScore() {
|
||||
return useMutation({
|
||||
mutationFn: (config: Partial<CreateProfileRequest>) => securityHeadersApi.calculateScore(config),
|
||||
});
|
||||
}
|
||||
|
||||
export function useValidateCSP() {
|
||||
return useMutation({
|
||||
mutationFn: (csp: string) => securityHeadersApi.validateCSP(csp),
|
||||
});
|
||||
}
|
||||
|
||||
export function useBuildCSP() {
|
||||
return useMutation({
|
||||
mutationFn: (directives: { directive: string; values: string[] }[]) =>
|
||||
securityHeadersApi.buildCSP(directives),
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user