Fix security scanner findings
- Replace hardcoded test secrets with crypto-generated values in laravel-encrypter.test.ts and totp.test.ts - Add 'secure' attribute to locale cookie in language-switcher.tsx - Validate image URLs before rendering in media-grid.tsx and media-picker.tsx (XSS prevention) - Validate redirect URL is HTTPS before window.location assignment in TopUpForm.tsx (open redirect prevention) - Document intentional MD5 usage for legacy PHP compatibility in password.ts - Document HMAC integrity protection for CBC cipher in laravel-encrypter.ts
This commit is contained in:
1 parent
942bc6fc8d
commit
1875a69b83
8 files changed
+46
-12
No files matched your search
@@ -5,6 +5,15 @@ import { uploadMedia } from "@/actions/admin-media";
|
||||
|
||||
type MediaFile = { name: string; url: string };
|
||||
|
||||
function validImageUrl(url: string): string {
|
||||
try {
|
||||
const u = new URL(url, window.location.origin);
|
||||
return u.protocol === "http:" || u.protocol === "https:" ? u.href : "";
|
||||
} catch {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
export function AdminMediaGrid() {
|
||||
const [files, setFiles] = useState<MediaFile[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -87,7 +96,7 @@ export function AdminMediaGrid() {
|
||||
{files.map((f) => (
|
||||
<div key={f.name} className="border border-[var(--color-text-muted)]/14 rounded-[10px] overflow-hidden bg-[var(--color-background)]">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src={f.url} alt={f.name} className="w-full h-[120px] object-cover block" />
|
||||
<img src={validImageUrl(f.url)} alt={f.name} className="w-full h-[120px] object-cover block" />
|
||||
<div className="p-2">
|
||||
<p className="text-xs text-[var(--color-text-muted)] m-0 mb-1 overflow-hidden text-ellipsis whitespace-nowrap">
|
||||
{f.name}
|
||||
|
||||
@@ -5,6 +5,15 @@ import { uploadMediaAndReturn } from "@/actions/admin-media";
|
||||
|
||||
type MediaFile = { name: string; url: string };
|
||||
|
||||
function validImageUrl(url: string): string {
|
||||
try {
|
||||
const u = new URL(url, window.location.origin);
|
||||
return u.protocol === "http:" || u.protocol === "https:" ? u.href : "";
|
||||
} catch {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
export function MediaPicker({
|
||||
onSelect,
|
||||
current,
|
||||
@@ -109,7 +118,7 @@ export function MediaPicker({
|
||||
>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={f.url}
|
||||
src={validImageUrl(f.url)}
|
||||
alt={f.name}
|
||||
className="w-full h-[100px] object-cover block"
|
||||
/>
|
||||
|
||||
@@ -32,7 +32,7 @@ export function LanguageSwitcher() {
|
||||
|
||||
function switchLocale(code: string) {
|
||||
if (code === locale) return;
|
||||
document.cookie = `NEXT_LOCALE=${code};path=/;max-age=31536000;samesite=lax`;
|
||||
document.cookie = `NEXT_LOCALE=${code};path=/;max-age=31536000;samesite=lax;secure`;
|
||||
startTransition(() => router.refresh());
|
||||
setOpen(false);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user