protect against sql injection, xss, csrf
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import { useAuth } from "../contexts/AuthContext";
|
||||
import PasswordStrengthMeter from "./PasswordStrengthMeter";
|
||||
|
||||
interface AuthModalProps {
|
||||
show: boolean;
|
||||
@@ -198,6 +199,9 @@ const AuthModal: React.FC<AuthModalProps> = ({
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
/>
|
||||
{mode === "signup" && (
|
||||
<PasswordStrengthMeter password={password} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
|
||||
interface LocationMapProps {
|
||||
latitude?: number;
|
||||
longitude?: number;
|
||||
location: string;
|
||||
itemName: string;
|
||||
}
|
||||
|
||||
const LocationMap: React.FC<LocationMapProps> = ({ latitude, longitude, location, itemName }) => {
|
||||
const mapRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
// If we have coordinates, use them directly
|
||||
if (latitude && longitude && mapRef.current) {
|
||||
// Create a simple map using an iframe with OpenStreetMap
|
||||
const mapUrl = `https://www.openstreetmap.org/export/embed.html?bbox=${longitude-0.01},${latitude-0.01},${longitude+0.01},${latitude+0.01}&layer=mapnik&marker=${latitude},${longitude}`;
|
||||
|
||||
mapRef.current.innerHTML = `
|
||||
<iframe
|
||||
width="100%"
|
||||
height="100%"
|
||||
frameborder="0"
|
||||
scrolling="no"
|
||||
marginheight="0"
|
||||
marginwidth="0"
|
||||
src="${mapUrl}"
|
||||
style="border: none; border-radius: 8px;"
|
||||
></iframe>
|
||||
`;
|
||||
} else if (location && mapRef.current) {
|
||||
// If we only have a location string, try to show it on the map
|
||||
// For a more robust solution, you'd want to use a geocoding service
|
||||
const encodedLocation = encodeURIComponent(location);
|
||||
const mapUrl = `https://www.openstreetmap.org/export/embed.html?bbox=-180,-90,180,90&layer=mapnik&marker=`;
|
||||
|
||||
// For now, we'll show a static map with a search link
|
||||
mapRef.current.innerHTML = `
|
||||
<div class="text-center p-4">
|
||||
<i class="bi bi-geo-alt-fill text-primary" style="font-size: 3rem;"></i>
|
||||
<p class="mt-2 mb-3"><strong>Location:</strong> ${location}</p>
|
||||
<a
|
||||
href="https://www.openstreetmap.org/search?query=${encodedLocation}"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="btn btn-sm btn-outline-primary"
|
||||
>
|
||||
<i class="bi bi-map me-2"></i>View on Map
|
||||
</a>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}, [latitude, longitude, location]);
|
||||
|
||||
return (
|
||||
<div className="mb-4">
|
||||
<h5>Location</h5>
|
||||
<div
|
||||
ref={mapRef}
|
||||
style={{
|
||||
height: '300px',
|
||||
backgroundColor: '#f8f9fa',
|
||||
borderRadius: '8px',
|
||||
overflow: 'hidden'
|
||||
}}
|
||||
>
|
||||
<div className="d-flex align-items-center justify-content-center h-100">
|
||||
<div className="spinner-border text-primary" role="status">
|
||||
<span className="visually-hidden">Loading map...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{(latitude && longitude) && (
|
||||
<p className="text-muted small mt-2">
|
||||
<i className="bi bi-info-circle me-1"></i>
|
||||
Exact location shown on map
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LocationMap;
|
||||
127
frontend/src/components/PasswordStrengthMeter.tsx
Normal file
127
frontend/src/components/PasswordStrengthMeter.tsx
Normal file
@@ -0,0 +1,127 @@
|
||||
import React from 'react';
|
||||
|
||||
interface PasswordStrengthMeterProps {
|
||||
password: string;
|
||||
showRequirements?: boolean;
|
||||
}
|
||||
|
||||
interface PasswordRequirement {
|
||||
regex: RegExp;
|
||||
text: string;
|
||||
met: boolean;
|
||||
}
|
||||
|
||||
const PasswordStrengthMeter: React.FC<PasswordStrengthMeterProps> = ({
|
||||
password,
|
||||
showRequirements = true
|
||||
}) => {
|
||||
const requirements: PasswordRequirement[] = [
|
||||
{
|
||||
regex: /.{8,}/,
|
||||
text: "At least 8 characters",
|
||||
met: /.{8,}/.test(password)
|
||||
},
|
||||
{
|
||||
regex: /[a-z]/,
|
||||
text: "One lowercase letter",
|
||||
met: /[a-z]/.test(password)
|
||||
},
|
||||
{
|
||||
regex: /[A-Z]/,
|
||||
text: "One uppercase letter",
|
||||
met: /[A-Z]/.test(password)
|
||||
},
|
||||
{
|
||||
regex: /\d/,
|
||||
text: "One number",
|
||||
met: /\d/.test(password)
|
||||
},
|
||||
{
|
||||
regex: /[@$!%*?&]/,
|
||||
text: "One special character (@$!%*?&)",
|
||||
met: /[@$!%*?&]/.test(password)
|
||||
}
|
||||
];
|
||||
|
||||
const getPasswordStrength = (): { score: number; label: string; color: string } => {
|
||||
if (!password) return { score: 0, label: '', color: '' };
|
||||
|
||||
const metRequirements = requirements.filter(req => req.met).length;
|
||||
const hasCommonPassword = ['password', '123456', '123456789', 'qwerty', 'abc123', 'password123'].includes(password.toLowerCase());
|
||||
|
||||
if (hasCommonPassword) {
|
||||
return { score: 0, label: 'Too Common', color: 'danger' };
|
||||
}
|
||||
|
||||
switch (metRequirements) {
|
||||
case 0:
|
||||
case 1:
|
||||
return { score: 1, label: 'Very Weak', color: 'danger' };
|
||||
case 2:
|
||||
return { score: 2, label: 'Weak', color: 'warning' };
|
||||
case 3:
|
||||
return { score: 3, label: 'Fair', color: 'info' };
|
||||
case 4:
|
||||
return { score: 4, label: 'Good', color: 'primary' };
|
||||
case 5:
|
||||
return { score: 5, label: 'Strong', color: 'success' };
|
||||
default:
|
||||
return { score: 0, label: '', color: '' };
|
||||
}
|
||||
};
|
||||
|
||||
const strength = getPasswordStrength();
|
||||
const progressPercentage = password ? (strength.score / 5) * 100 : 0;
|
||||
|
||||
if (!password) return null;
|
||||
|
||||
return (
|
||||
<div className="password-strength-meter mt-2">
|
||||
{/* Strength Bar */}
|
||||
<div className="mb-2">
|
||||
<div className="d-flex justify-content-between align-items-center mb-1">
|
||||
<small className="text-muted">Password Strength</small>
|
||||
{strength.label && (
|
||||
<small className={`text-${strength.color} fw-bold`}>
|
||||
{strength.label}
|
||||
</small>
|
||||
)}
|
||||
</div>
|
||||
<div className="progress" style={{ height: '4px' }}>
|
||||
<div
|
||||
className={`progress-bar bg-${strength.color}`}
|
||||
role="progressbar"
|
||||
style={{ width: `${progressPercentage}%` }}
|
||||
aria-valuenow={progressPercentage}
|
||||
aria-valuemin={0}
|
||||
aria-valuemax={100}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Requirements List */}
|
||||
{showRequirements && (
|
||||
<div className="password-requirements">
|
||||
<small className="text-muted d-block mb-1">Password must contain:</small>
|
||||
<ul className="list-unstyled mb-0" style={{ fontSize: '0.75rem' }}>
|
||||
{requirements.map((requirement, index) => (
|
||||
<li key={index} className="d-flex align-items-center mb-1">
|
||||
<i
|
||||
className={`bi ${
|
||||
requirement.met ? 'bi-check-circle-fill text-success' : 'bi-circle text-muted'
|
||||
} me-2`}
|
||||
style={{ fontSize: '0.75rem' }}
|
||||
/>
|
||||
<span className={requirement.met ? 'text-success' : 'text-muted'}>
|
||||
{requirement.text}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PasswordStrengthMeter;
|
||||
Reference in New Issue
Block a user