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:
openhands committed 2026-07-10 23:08:15 +02:00
1 parent 942bc6fc8d
commit 1875a69b83
8 files changed
+46 -12

No files matched your search

+10 -1
View File
@@ -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}
+10 -1
View File
@@ -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"
/>
+1 -1
View File
@@ -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);
}