Skip to content

Commit 0298ea5

Browse files
Implement security fixes
1 parent e964b9f commit 0298ea5

File tree

6 files changed

+621
-7
lines changed

6 files changed

+621
-7
lines changed

src/components/Dashboard.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,10 @@ export const Dashboard = memo(() => {
262262
// 🆕 Détecter une nouvelle campagne créée et afficher la modale
263263
useEffect(() => {
264264
const checkForNewCampaign = () => {
265-
const newCampaignCreated = localStorage.getItem('newCampaignCreated');
265+
let newCampaignCreated: any = null;
266+
import('@/utils/secureClientStorage').then(({ secureStorage }) => {
267+
newCampaignCreated = secureStorage.getSecure('newCampaignCreated');
268+
});
266269
if (newCampaignCreated) {
267270
try {
268271
const campaignData = JSON.parse(newCampaignCreated);

src/hooks/useCampaignFormSubmission.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ export const useCampaignFormSubmission = (
2424
console.log('🎯 NOUVEAU FLOW: Redirection vers Stripe SANS créer la campagne');
2525

2626
try {
27-
// Stocker les données de campagne dans localStorage pour après validation Stripe
28-
localStorage.setItem('pendingCampaignData', JSON.stringify(campaignData));
29-
console.log('💾 Données campagne stockées dans localStorage');
27+
// Store campaign data securely with encryption
28+
import('@/utils/secureClientStorage').then(({ secureStorage }) => {
29+
secureStorage.setCampaignData('pendingCampaignData', campaignData, 2); // 2 hours expiry
30+
console.log('🔒 Campaign data stored securely');
31+
});
3032

3133
// Stocker aussi dans le state pour le flow normal
3234
setPendingCampaignData(campaignData);
@@ -36,7 +38,9 @@ export const useCampaignFormSubmission = (
3638
console.log('✅ Redirection vers Stripe en cours (campagne PAS ENCORE créée)...');
3739
} catch (error) {
3840
console.error('❌ Erreur lors de la redirection vers Stripe:', error);
39-
localStorage.removeItem('pendingCampaignData');
41+
import('@/utils/secureClientStorage').then(({ secureStorage }) => {
42+
secureStorage.removeSecure('campaign_pendingCampaignData');
43+
});
4044
setPendingCampaignData(null);
4145
toast({
4246
title: "Erreur",

src/hooks/useCampaignsSupabase.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ export const useCampaignsSupabase = () => {
128128
console.log('✅ SUPABASE - Campagne créée:', newCampaign.id);
129129

130130
// Store campaign data for success modal
131-
localStorage.setItem('newCampaignCreated', JSON.stringify({
131+
import('@/utils/secureClientStorage').then(({ secureStorage }) => {
132+
secureStorage.setSecure('newCampaignCreated', {
132133
id: newCampaign.id,
133134
name: newCampaign.name
134135
}));
Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
/**
2+
* 🛡️ Security Integration Hook
3+
* Integrates client-side security with backend validation
4+
*/
5+
6+
import { useCallback } from 'react';
7+
import { supabase } from '@/integrations/supabase/client';
8+
import { securityMonitoring } from '@/utils/security';
9+
10+
export const useSecurityIntegration = () => {
11+
12+
/**
13+
* Validate input with server-side validation
14+
*/
15+
const validateInput = useCallback(async (data: any) => {
16+
try {
17+
const { data: validationResult, error } = await supabase.functions.invoke(
18+
'security-validation',
19+
{
20+
body: {
21+
action: 'validate_input',
22+
data,
23+
clientInfo: {
24+
ip: 'client', // Will be populated by edge function
25+
userAgent: navigator.userAgent,
26+
timestamp: Date.now()
27+
}
28+
}
29+
}
30+
);
31+
32+
if (error) {
33+
console.error('🚨 SECURITY: Input validation error:', error);
34+
securityMonitoring.logSuspiciousActivity({
35+
type: 'validation_service_error',
36+
details: { error: error.message, data: JSON.stringify(data).substring(0, 200) }
37+
});
38+
return { isValid: false, errors: ['Validation service error'] };
39+
}
40+
41+
if (!validationResult.isValid) {
42+
securityMonitoring.logSuspiciousActivity({
43+
type: 'invalid_input_detected',
44+
details: {
45+
errors: validationResult.errors,
46+
dataType: typeof data,
47+
inputLength: JSON.stringify(data).length
48+
}
49+
});
50+
}
51+
52+
return validationResult;
53+
54+
} catch (error) {
55+
console.error('🚨 SECURITY: Validation request failed:', error);
56+
// Fail closed - reject invalid input
57+
return { isValid: false, errors: ['Security validation failed'] };
58+
}
59+
}, []);
60+
61+
/**
62+
* Check permissions with server-side validation
63+
*/
64+
const checkPermissions = useCallback(async (resourceId: string, resourceType: string) => {
65+
try {
66+
const { data: permissionResult, error } = await supabase.functions.invoke(
67+
'security-validation',
68+
{
69+
body: {
70+
action: 'check_permissions',
71+
resourceId,
72+
resourceType,
73+
clientInfo: {
74+
ip: 'client',
75+
userAgent: navigator.userAgent,
76+
timestamp: Date.now()
77+
}
78+
}
79+
}
80+
);
81+
82+
if (error) {
83+
console.error('🚨 SECURITY: Permission check error:', error);
84+
securityMonitoring.logSuspiciousActivity({
85+
type: 'permission_check_error',
86+
details: {
87+
error: error.message,
88+
resourceId,
89+
resourceType
90+
}
91+
});
92+
return { hasPermission: false };
93+
}
94+
95+
if (!permissionResult.hasPermission) {
96+
securityMonitoring.logSuspiciousActivity({
97+
type: 'unauthorized_access_attempt',
98+
details: {
99+
resourceId,
100+
resourceType,
101+
details: permissionResult.details
102+
}
103+
});
104+
}
105+
106+
return permissionResult;
107+
108+
} catch (error) {
109+
console.error('🚨 SECURITY: Permission check failed:', error);
110+
// Fail closed - deny access
111+
return { hasPermission: false };
112+
}
113+
}, []);
114+
115+
/**
116+
* Log security event to server
117+
*/
118+
const logSecurityEvent = useCallback(async (eventType: string, eventData: any) => {
119+
try {
120+
const { error } = await supabase.functions.invoke(
121+
'security-validation',
122+
{
123+
body: {
124+
action: 'log_security_event',
125+
eventType,
126+
eventData,
127+
clientInfo: {
128+
ip: 'client',
129+
userAgent: navigator.userAgent,
130+
timestamp: Date.now()
131+
}
132+
}
133+
}
134+
);
135+
136+
if (error) {
137+
console.error('🚨 SECURITY: Failed to log security event:', error);
138+
// Continue execution - logging failure shouldn't block operations
139+
}
140+
141+
} catch (error) {
142+
console.error('🚨 SECURITY: Security event logging failed:', error);
143+
}
144+
}, []);
145+
146+
/**
147+
* Check rate limits
148+
*/
149+
const checkRateLimit = useCallback(async () => {
150+
try {
151+
const { data: rateLimitResult, error } = await supabase.functions.invoke(
152+
'security-validation',
153+
{
154+
body: {
155+
action: 'check_rate_limit',
156+
clientInfo: {
157+
ip: 'client',
158+
userAgent: navigator.userAgent,
159+
timestamp: Date.now()
160+
}
161+
}
162+
}
163+
);
164+
165+
if (error) {
166+
console.error('🚨 SECURITY: Rate limit check error:', error);
167+
// Assume rate limit exceeded on error for safety
168+
return { allowed: false, error: 'Rate limit check failed' };
169+
}
170+
171+
if (!rateLimitResult.allowed) {
172+
securityMonitoring.logSuspiciousActivity({
173+
type: 'rate_limit_exceeded',
174+
details: rateLimitResult
175+
});
176+
}
177+
178+
return rateLimitResult;
179+
180+
} catch (error) {
181+
console.error('🚨 SECURITY: Rate limit check failed:', error);
182+
return { allowed: false, error: 'Rate limit check failed' };
183+
}
184+
}, []);
185+
186+
/**
187+
* Comprehensive security check for sensitive operations
188+
*/
189+
const performSecurityCheck = useCallback(async (
190+
operation: string,
191+
data?: any,
192+
resourceId?: string,
193+
resourceType?: string
194+
) => {
195+
console.log(`🔐 SECURITY: Starting comprehensive security check for ${operation}`);
196+
197+
// 1. Check rate limits
198+
const rateLimitResult = await checkRateLimit();
199+
if (!rateLimitResult.allowed) {
200+
return {
201+
allowed: false,
202+
reason: 'rate_limit_exceeded',
203+
details: rateLimitResult
204+
};
205+
}
206+
207+
// 2. Validate input if provided
208+
if (data) {
209+
const validationResult = await validateInput(data);
210+
if (!validationResult.isValid) {
211+
return {
212+
allowed: false,
213+
reason: 'invalid_input',
214+
details: validationResult
215+
};
216+
}
217+
}
218+
219+
// 3. Check permissions if resource specified
220+
if (resourceId && resourceType) {
221+
const permissionResult = await checkPermissions(resourceId, resourceType);
222+
if (!permissionResult.hasPermission) {
223+
return {
224+
allowed: false,
225+
reason: 'insufficient_permissions',
226+
details: permissionResult
227+
};
228+
}
229+
}
230+
231+
// 4. Log successful security check
232+
await logSecurityEvent('security_check_passed', {
233+
operation,
234+
hasData: !!data,
235+
hasResource: !!(resourceId && resourceType),
236+
rateLimitRemaining: rateLimitResult.remaining
237+
});
238+
239+
return {
240+
allowed: true,
241+
reason: 'security_check_passed',
242+
details: {
243+
rateLimit: rateLimitResult
244+
}
245+
};
246+
}, [validateInput, checkPermissions, checkRateLimit, logSecurityEvent]);
247+
248+
return {
249+
validateInput,
250+
checkPermissions,
251+
logSecurityEvent,
252+
checkRateLimit,
253+
performSecurityCheck
254+
};
255+
};

0 commit comments

Comments
 (0)