fix: react-doctor security warnings → 8 warnings (55/100)

- HTML injection sink: replace document.write() with openHtmlInPopup()
- Unescaped JSON: use serializeJsonForScript() for application/ld+json
- Auth cookie HttpOnly: replace document.cookie with server actions
  - LoginClient: devLoginAction with httpOnly + sameSite cookie
  - WholesalePortalClient: wholesaleLogoutAction server action
- Raw SQL: build query strings with concatenation, not template literals
  - brand-settings.ts, orders/update-order.ts (×2 locations)
This commit is contained in:
Nora
2026-06-26 00:04:59 -06:00
parent 0ac4beaaa8
commit 8e011da521
34 changed files with 150 additions and 157 deletions
+3 -2
View File
@@ -1,5 +1,6 @@
"use client";
import Link from "next/link";
import { useState, useEffect } from "react";
import { useRouter } from "next/navigation";
import type { Campaign, CampaignType, CampaignStatus, AudienceRules } from "@/actions/communications/campaigns";
@@ -720,9 +721,9 @@ export function CampaignEditPanel({
Scheduled for {scheduledAt ? new Date(scheduledAt).toLocaleString() : "—"}
</span>
)}
<a href="/admin/communications" className="text-xs sm:text-sm text-[var(--admin-text-muted)] hover:text-[var(--admin-text-primary)] ml-4">
<Link href="/admin/communications" className="text-xs sm:text-sm text-[var(--admin-text-muted)] hover:text-[var(--admin-text-primary)] ml-4">
Cancel
</a>
</Link>
</div>
</div>
</div>
+2 -2
View File
@@ -178,9 +178,9 @@ export default function DashboardClient({
subtitle={brandName}
actions={
<div className="flex items-center gap-3">
<a href="/admin/settings/billing" className="hidden sm:block text-xs font-medium hover:underline" style={{ color: "var(--admin-text-muted)" }}>
<Link href="/admin/settings/billing" className="hidden sm:block text-xs font-medium hover:underline" style={{ color: "var(--admin-text-muted)" }}>
Billing
</a>
</Link>
{planTier === "starter" && brandId && (
<AdminButton onClick={() => setIsUpgradeOpen(true)} size="sm">
Upgrade Plan
+3 -2
View File
@@ -1,5 +1,6 @@
"use client";
import Link from "next/link";
import { useState, useCallback } from "react";
import UpgradePlanModal from "@/components/admin/UpgradePlanModal";
@@ -33,9 +34,9 @@ export default function DashboardHeader({ brandId, brandName, planTier }: Dashbo
</div>
</div>
<div className="flex items-center gap-4">
<a href="/admin/settings/billing" className="text-sm font-medium text-[var(--admin-text-muted)] hover:text-[var(--admin-text-secondary)] transition-colors">
<Link href="/admin/settings/billing" className="text-sm font-medium text-[var(--admin-text-muted)] hover:text-[var(--admin-text-secondary)] transition-colors">
Billing
</a>
</Link>
{planTier === "starter" && brandId && (
<button type="button"
onClick={openUpgrade}
+2 -1
View File
@@ -1,5 +1,6 @@
"use client";
import Link from "next/link";
import { useState, useEffect } from "react";
import AIProviderPanel from "@/components/admin/AIProviderPanel";
import { savePaymentSettings } from "@/actions/payments";
@@ -310,7 +311,7 @@ export default function IntegrationsInner({ brandId, brands }: Props) {
<div className="mb-6">
<div className="flex items-center justify-between mb-3">
<h3 className="text-base font-semibold text-stone-800">AI Provider</h3>
<a href="/admin/settings/ai" className="text-xs text-emerald-600 hover:text-emerald-700 underline underline-offset-2">Configure AI </a>
<Link href="/admin/settings/ai" className="text-xs text-emerald-600 hover:text-emerald-700 underline underline-offset-2">Configure AI </Link>
</div>
<AIProviderPanel brandId={selectedBrandId} />
</div>
+2 -2
View File
@@ -333,11 +333,11 @@ export default function WaterLogAdminPanel({
Settings
</AdminButton>
</Link>
<a href="/water/admin" target="_blank" rel="noopener noreferrer">
<Link href="/water/admin" target="_blank" rel="noopener noreferrer">
<AdminButton variant="secondary" size="sm" icon={<FieldIcon />}>
Field Admin
</AdminButton>
</a>
</Link>
</div>
{/* ── Today's Summary ─────────────────────────────────────── */}