protect against sql injection, xss, csrf

This commit is contained in:
jackiettran
2025-09-16 12:27:15 -04:00
parent ce0b7bd0cc
commit a9fa579b6d
10 changed files with 1311 additions and 103 deletions

View File

@@ -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

View File

@@ -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;

View 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;